<commit_before>/*!
 * @license ltsv Copyright(c) 2013 sasa+1
 * https://github.com/sasaplus1/ltsv.js
 * Released under the MIT License.
 */

/**
 * main function.
 *
 * @param {*} global global object.
 */
(function(global) {

  var parser, formatter, validator,
      util, stream;

  /** LTSV parser. */
  parser = (function() {
    /**
     * split from LTSV to records.
     *
     * @param {String} text LTSV string.
     * @return {Object[]} object has fields.
     */
    function parse(text) {
      return parse_(text, parseLine);
    }

    /**
     * split from record to fields.
     *
     * @param {String} line record string.
     * @return {Object} object has fields.
     */
    function parseLine(line) {
      return parseLine_(line, splitField_);
    }

    /**
     * split from LTSV to records and validate label and value of fields.
     *
     * @param {String} text LTSV string.
     * @return {Object[]} object has fields.
     */
    function parseStrict(text) {
      return parse_(text, parseLineStrict);
    }

    /**
     * split from record to fields and validate label and value of fields.
     *
     * @param {String} line record string.
     * @return {Object} object has fields.
     */
    function parseLineStrict(line) {
      return parseLine_(line, splitFieldStrict_);
    }

    /**
     * base of parse function.
     *
     * @private
     * @param {String} line record string.
     * @param {Function} fn line parse function.
     * @return {Object[]} parsed records.
     */
    function parse_(text, fn) {
      var lines = String(text).replace(/(?:\r?\n)+$/, '').split(/\r?\n/),
          records = [],
          i, len;

      for (i = 0, len = lines.length; i < len; ++i) {
        records[i] = fn(lines[i]);
      }

      return records;
    }

    /**
     * base of parseLine function.
     *
     * @private
     * @param {String} line record string.
     * @param {Function} fn field split function.
     * @return {Object} parsed record.
     */
    function parseLine_(line, fn) {
      var fields = String(line).replace(/(?:\r?\n)+$/, '').split('\t'),
          record = {},
          i, len, field;

      for (i = 0, len = fields.length; i < len; ++i) {
        field = fn(fields[i]);
        record[field.label] = field.value;
      }

      return record;
    }

    /**
     * split to label and value from field.
     *
     * @private
     * @param {String} chunk field string.
     * @throws {SyntaxError} if chunk not has separator.
     * @return {Object} parsed field.
     */
    function splitField_(chunk) {
      var field = String(chunk),
          separatorIndex = field.indexOf(':');

      if (separatorIndex === -1) {
        throw new SyntaxError('field separator not found: "' + field + '"');
      }

      return {
        label: field.slice(0, separatorIndex),
        value: field.slice(separatorIndex + 1)
      };
    }

    /**
     * split to label and value from field and validate label and value.
     *
     * @private
     * @param {String} chunk field string.
     * @throws {SyntaxError} if label or value has unexpected character.
     * @return {Object} parsed field.
     */
    function splitFieldStrict_(chunk) {
      var field = splitField_(chunk);

      if (!validator.isValidLabel(field.label)) {
        throw new SyntaxError(
            'unexpected character for label: "' + field.label + '"');
      }

      if (!validator.isValidValue(field.value)) {
        throw new SyntaxError(
            'unexpected character for value: "' + field.value + '"');
      }

      return field;
    }

    return {
      parse: parse,
      parseLine: parseLine,
      parseStrict: parseStrict,
      parseLineStrict: parseLineStrict,
      parse_: parse_,
      parseLine_: parseLine_,
      splitField_: splitField_,
      splitFieldStrict_: splitFieldStrict_
    };
  }());

  /** LTSV formatter. */
  formatter = (function() {
    /**
     * format to LTSV from object or object array.
     *
     * @param {Object|Object[]} mixed object or object array.
     * @throws {TypeError} if parameter is not a Object
     * @return {String} LTSV string.
     */
    function format(mixed) {
      var mixedType = getType_(mixed),
          records = [],
          i, len;

      if (mixedType !== 'object') {
        throw new TypeError(
            'parameter should be an Object or Array: ' + mixedType);
      }

      if (util.isArray(mixed)) {
        for (i = 0, len = mixed.length; i < len; ++i) {
          records[i] = objectToRecord_(mixed[i]);
        }
      } else {
        records.push(objectToRecord_(mixed));
      }

      return records.join('\n');
    }

    /**
     * format to LTSV from object or object array.
     * validate label and value.
     *
     * @param {Object|Object[]} mixed object or object array.
     * @throws {TypeError} if parameter is not a Object
     * @return {String} LTSV string.
     */
    function formatStrict(mixed) {
      var mixedType = getType_(mixed),
          records = [],
          i, len;

      if (mixedType !== 'object') {
        throw new TypeError(
            'parameter should be an Object or Array: ' + mixedType);
      }

      if (util.isArray(mixed)) {
        for (i = 0, len = mixed.length; i < len; ++i) {
          records[i] = objectToRecordStrict_(mixed[i]);
        }
      } else {
        records.push(objectToRecordStrict_(mixed));
      }

      return records.join('\n');
    }

    /**
     * format to record from object.
     *
     * @private
     * @param {Object} object object.
     * @throws {TypeError} if parameter is not a Object
     * @return {String} record string.
     */
    function objectToRecord_(object) {
      var objectType = getType_(object),
          keys, fields, i, len;

      if (objectType !== 'object') {
        throw new TypeError(
            'parameter should be an Object: ' + objectType);
      }

      keys = Object.keys(object);
      fields = [];

      for (i = 0, len = keys.length; i < len; ++i) {
        fields[i] = keys[i] + ':' + object[keys[i]];
      }

      return fields.join('\t');
    }

    /**
     * format to record from object and validate label and value.
     *
     * @private
     * @param {Object} object object.
     * @throws {TypeError} if parameter is not a Object
     * @throws {SyntaxError} if label or value has unexpected character
     * @return {String} record string.
     */
    function objectToRecordStrict_(object) {
      var objectType = getType_(object),
          keys, fields, i, len, label, value;

      if (objectType !== 'object') {
        throw new TypeError(
            'parameter should be an Object: ' + objectType);
      }

      keys = Object.keys(object);
      fields = [];

      for (i = 0, len = keys.length; i < len; ++i) {
        label = keys[i];
        value = object[label];

        if (!validator.isValidLabel(label)) {
          throw new SyntaxError(
              'unexpected character for label: "' + label + '"');
        }

        if (!validator.isValidValue(value)) {
          throw new SyntaxError(
              'unexpected character for value: "' + value + '"');
        }

        fields[i] = label + ':' + value;
      }

      return fields.join('\t');
    }

    /**
     * get type of value.
     * return "null" if value is null.
     * otherwise return typeof value.
     *
     * @param {*} value value.
     * @return {String} type of value.
     */
    function getType_(value) {
      return (value === null) ? 'null' : typeof value;
    }

    return {
      format: format,
      formatStrict: formatStrict,
      objectToRecord_: objectToRecord_,
      objectToRecordStrict_: objectToRecordStrict_,
      getType_: getType_
    };
  }());

  /** field validator. */
  validator = (function() {
    var labelRegExp = /^[0-9A-Za-z_.-]+$/,
        valueRegExp = /^[\x01-\x08\x0B\x0C\x0E-\xFF]*$/;

    /**
     * validate label.
     *
     * @param {String} label label string.
     * @return {Boolean} return true if validation success.
     */
    function isValidLabel(label) {
      return labelRegExp.test(label);
    }

    /**
     * validate value.
     *
     * @param {String} value value string.
     * @return {Boolean} return true if validation success.
     */
    function isValidValue(value) {
      return valueRegExp.test(value);
    }

    return {
      isValidLabel: isValidLabel,
      isValidValue: isValidValue
    };
  }());

  /**
   * utility function.
   * use util module if node.js env.
   */
  util = (typeof module !== 'undefined') ? require('util') : (function() {
    /**
     * check value is Array.
     *
     * @param {*} value value.
     * @return {Boolean} return true if value is Array.
     */
    function isArray_(value) {
      return (Object.prototype.toString.call(value) === '[object Array]');
    }

    return {
      /** use Array#isArray if implemented. */
      isArray: Array.isArray || isArray_
    };
  }());

  /**
   * stream for convert from LTSV to JSON.
   * only implement if node.js env.
   */
  (typeof module !== 'undefined') && (stream = (function() {
    var stream = require('stream'),
        stringDecoder = require('string_decoder').StringDecoder;

    /** inherit from TransformStream */
    util.inherits(LtsvToJsonStream, stream.Transform);

    /**
     * constructor of LtsvToJsonStream.
     *
     * @private
     * @constructor
     * @param {Object} options options object.
     */
    function LtsvToJsonStream(options) {
      options.objectMode = true;
      options.decodeStrings = true;

      stream.Transform.call(this, options);

      this.toObject_ = options.toObject || false;
      this.isStrict_ = options.strict || false;

      this.parseLine_ =
          (this.isStrict_) ? parser.parseLineStrict : parser.parseLine;

      this.buffer_ = '';
      this.splits_ = [];
      this.decoder_ = new stringDecoder(options.encoding || 'utf8');
    }

    /**
     * transform chunk.
     *
     * @param {Buffer|Array} chunk chunk string.
     * @param {String} encoding encoding of chunk.
     * @param {Function} callback callback function.
     */
    LtsvToJsonStream.prototype._transform =
        function(chunk, encoding, callback) {
      var text = this.buffer_ + this.decoder_.write(chunk),
          data = this.splitToLines_(text);

      this.buffer_ = data.tail;
      this.splits_ = data.lines;
      this.append_(this.splits_, callback);
    };

    /**
     * transform end of chunk.
     *
     * @param {Function} callback callback function.
     */
    LtsvToJsonStream.prototype._flush = function(callback) {
      var text = this.buffer_ + this.decoder_.end(),
          data = this.splitToLines_(text);

      // not append if end of line is empty.
      if (data.tail === '') {
        this.splits_ = this.splits_.concat(data.lines);
      } else {
        this.splits_ = this.splits_.concat(data.lines, [data.tail]);
      }

      this.buffer_ = '';
      this.appendAll_(this.splits_, callback);
    };

    /**
     * append records to stream.
     *
     * @private
     * @param {String[]} lines line strings.
     * @param {Function} callback callback function.
     * @return {Undefined} undefined.
     */
    LtsvToJsonStream.prototype.append_ = function(lines, callback) {
      var i, len, line, record;

      try {
        for (i = 0, len = lines.length; i < len; ++i) {
          line = lines.shift();
          record = this.parseLine_(line);

          if (!this.toObject_) {
            record = JSON.stringify(record);
          }

          if (!this.push(record)) {
            lines.unshift(line);
            break;
          }
        }
      } catch (e) {
        return this.emit('error', e);
      }

      (typeof callback === 'function') && callback(null);
    };

    /**
     * append all records to stream.
     *
     * @private
     * @param {String[]} lines line strings.
     * @param {Function} callback callback function.
     */
    LtsvToJsonStream.prototype.appendAll_ = function(lines, callback) {
      this.append_(lines);

      if (lines.length > 0) {
        setImmediate(function(that) {
          that.appendAll_(lines, callback);
        }, this);
      } else {
        this.push(null);
        (typeof callback === 'function') && callback(null);
      }
    };

    /**
     * split to lines from text.
     *
     * @private
     * @param {String} text text string.
     * @return {Object} lines and tail text.
     */
    LtsvToJsonStream.prototype.splitToLines_ = function(text) {
      var curr = 0,
          prev = 0,
          lines = [];

      while ((curr = text.indexOf('\n', prev)) !== -1) {
        lines.push(text.slice(prev, curr));
        prev = curr + 1;
      }

      return {
        lines: lines,
        tail: text.slice(prev)
      };
    };

    /**
     * return new LtsvToJsonStream instance.
     *
     * @param {Object} options option object.
     * @return {LtsvToJsonStream} LtsvToJsonStream instance.
     */
    function createLtsvToJsonStream(options) {
      return new LtsvToJsonStream(options || {});
    }

    return {
      createLtsvToJsonStream: createLtsvToJsonStream
    };
  }()));

  /** if node.js. */
  if (typeof module !== 'undefined') {
    /** export functions for node.js. */
    module.exports = {
      parse: parser.parse,
      parseLine: parser.parseLine,
      parseStrict: parser.parseStrict,
      parseLineStrict: parser.parseLineStrict,
      format: formatter.format,
      formatStrict: formatter.formatStrict,
      createLtsvToJsonStream: stream.createLtsvToJsonStream
    };

    /** export private functions if NODE_ENV variable is test. */
    if (process.env.NODE_ENV === 'test') {
      module.exports.parser_ = parser;
      module.exports.formatter_ = formatter;
      module.exports.validator_ = validator;
      module.exports.util_ = util;
    }
  } else {
    /** export functions for browser. */
    global.ltsv = {
      parse: parser.parse,
      parseLine: parser.parseLine,
      parseStrict: parser.parseStrict,
      parseLineStrict: parser.parseLineStrict,
      format: formatter.format,
      formatStrict: formatter.formatStrict
    };

    /** export private functions if declared Mocha. */
    if (typeof Mocha !== 'undefined') {
      global.ltsv.parser_ = parser;
      global.ltsv.formatter_ = formatter;
      global.ltsv.validator_ = validator;
      global.ltsv.util_ = util;
    }
  }

}(this));
<commit_msg>fixed coding style<commit_after>/*!
 * @license ltsv Copyright(c) 2013 sasa+1
 * https://github.com/sasaplus1/ltsv.js
 * Released under the MIT License.
 */

/**
 * main function.
 *
 * @param {*} global global object.
 */
(function(global) {

  var parser, formatter, validator,
      util, stream;

  /** LTSV parser. */
  parser = (function() {
    /**
     * split from LTSV to records.
     *
     * @param {String} text LTSV string.
     * @return {Object[]} object has fields.
     */
    function parse(text) {
      return parse_(text, parseLine);
    }

    /**
     * split from record to fields.
     *
     * @param {String} line record string.
     * @return {Object} object has fields.
     */
    function parseLine(line) {
      return parseLine_(line, splitField_);
    }

    /**
     * split from LTSV to records and validate label and value of fields.
     *
     * @param {String} text LTSV string.
     * @return {Object[]} object has fields.
     */
    function parseStrict(text) {
      return parse_(text, parseLineStrict);
    }

    /**
     * split from record to fields and validate label and value of fields.
     *
     * @param {String} line record string.
     * @return {Object} object has fields.
     */
    function parseLineStrict(line) {
      return parseLine_(line, splitFieldStrict_);
    }

    /**
     * base of parse function.
     *
     * @private
     * @param {String} line record string.
     * @param {Function} fn line parse function.
     * @return {Object[]} parsed records.
     */
    function parse_(text, fn) {
      var lines = String(text).replace(/(?:\r?\n)+$/, '').split(/\r?\n/),
          records = [],
          i, len;

      for (i = 0, len = lines.length; i < len; ++i) {
        records[i] = fn(lines[i]);
      }

      return records;
    }

    /**
     * base of parseLine function.
     *
     * @private
     * @param {String} line record string.
     * @param {Function} fn field split function.
     * @return {Object} parsed record.
     */
    function parseLine_(line, fn) {
      var fields = String(line).replace(/(?:\r?\n)+$/, '').split('\t'),
          record = {},
          i, len, field;

      for (i = 0, len = fields.length; i < len; ++i) {
        field = fn(fields[i]);
        record[field.label] = field.value;
      }

      return record;
    }

    /**
     * split to label and value from field.
     *
     * @private
     * @param {String} chunk field string.
     * @throws {SyntaxError} if chunk not has separator.
     * @return {Object} parsed field.
     */
    function splitField_(chunk) {
      var field = String(chunk),
          separatorIndex = field.indexOf(':');

      if (separatorIndex === -1) {
        throw new SyntaxError('field separator not found: "' + field + '"');
      }

      return {
        label: field.slice(0, separatorIndex),
        value: field.slice(separatorIndex + 1)
      };
    }

    /**
     * split to label and value from field and validate label and value.
     *
     * @private
     * @param {String} chunk field string.
     * @throws {SyntaxError} if label or value has unexpected character.
     * @return {Object} parsed field.
     */
    function splitFieldStrict_(chunk) {
      var field = splitField_(chunk);

      if (!validator.isValidLabel(field.label)) {
        throw new SyntaxError(
            'unexpected character for label: "' + field.label + '"');
      }

      if (!validator.isValidValue(field.value)) {
        throw new SyntaxError(
            'unexpected character for value: "' + field.value + '"');
      }

      return field;
    }

    return {
      parse: parse,
      parseLine: parseLine,
      parseStrict: parseStrict,
      parseLineStrict: parseLineStrict,
      parse_: parse_,
      parseLine_: parseLine_,
      splitField_: splitField_,
      splitFieldStrict_: splitFieldStrict_
    };
  }());

  /** LTSV formatter. */
  formatter = (function() {
    /**
     * format to LTSV from object or object array.
     *
     * @param {Object|Object[]} mixed object or object array.
     * @throws {TypeError} if parameter is not a Object
     * @return {String} LTSV string.
     */
    function format(mixed) {
      var mixedType = getType_(mixed),
          records = [],
          i, len;

      if (mixedType !== 'object') {
        throw new TypeError(
            'parameter should be an Object or Array: ' + mixedType);
      }

      if (util.isArray(mixed)) {
        for (i = 0, len = mixed.length; i < len; ++i) {
          records[i] = objectToRecord_(mixed[i]);
        }
      } else {
        records.push(objectToRecord_(mixed));
      }

      return records.join('\n');
    }

    /**
     * format to LTSV from object or object array.
     * validate label and value.
     *
     * @param {Object|Object[]} mixed object or object array.
     * @throws {TypeError} if parameter is not a Object
     * @return {String} LTSV string.
     */
    function formatStrict(mixed) {
      var mixedType = getType_(mixed),
          records = [],
          i, len;

      if (mixedType !== 'object') {
        throw new TypeError(
            'parameter should be an Object or Array: ' + mixedType);
      }

      if (util.isArray(mixed)) {
        for (i = 0, len = mixed.length; i < len; ++i) {
          records[i] = objectToRecordStrict_(mixed[i]);
        }
      } else {
        records.push(objectToRecordStrict_(mixed));
      }

      return records.join('\n');
    }

    /**
     * format to record from object.
     *
     * @private
     * @param {Object} object object.
     * @throws {TypeError} if parameter is not a Object
     * @return {String} record string.
     */
    function objectToRecord_(object) {
      var objectType = getType_(object),
          keys, fields, i, len;

      if (objectType !== 'object') {
        throw new TypeError(
            'parameter should be an Object: ' + objectType);
      }

      keys = Object.keys(object);
      fields = [];

      for (i = 0, len = keys.length; i < len; ++i) {
        fields[i] = keys[i] + ':' + object[keys[i]];
      }

      return fields.join('\t');
    }

    /**
     * format to record from object and validate label and value.
     *
     * @private
     * @param {Object} object object.
     * @throws {TypeError} if parameter is not a Object
     * @throws {SyntaxError} if label or value has unexpected character
     * @return {String} record string.
     */
    function objectToRecordStrict_(object) {
      var objectType = getType_(object),
          keys, fields, i, len, label, value;

      if (objectType !== 'object') {
        throw new TypeError(
            'parameter should be an Object: ' + objectType);
      }

      keys = Object.keys(object);
      fields = [];

      for (i = 0, len = keys.length; i < len; ++i) {
        label = keys[i];
        value = object[label];

        if (!validator.isValidLabel(label)) {
          throw new SyntaxError(
              'unexpected character for label: "' + label + '"');
        }

        if (!validator.isValidValue(value)) {
          throw new SyntaxError(
              'unexpected character for value: "' + value + '"');
        }

        fields[i] = label + ':' + value;
      }

      return fields.join('\t');
    }

    /**
     * get type of value.
     * return "null" if value is null.
     * otherwise return typeof value.
     *
     * @private
     * @param {*} value value.
     * @return {String} type of value.
     */
    function getType_(value) {
      return (value === null) ? 'null' : typeof value;
    }

    return {
      format: format,
      formatStrict: formatStrict,
      objectToRecord_: objectToRecord_,
      objectToRecordStrict_: objectToRecordStrict_,
      getType_: getType_
    };
  }());

  /** field validator. */
  validator = (function() {
    var labelRegExp = /^[0-9A-Za-z_.-]+$/,
        valueRegExp = /^[\x01-\x08\x0B\x0C\x0E-\xFF]*$/;

    /**
     * validate label.
     *
     * @param {String} label label string.
     * @return {Boolean} return true if validation success.
     */
    function isValidLabel(label) {
      return labelRegExp.test(label);
    }

    /**
     * validate value.
     *
     * @param {String} value value string.
     * @return {Boolean} return true if validation success.
     */
    function isValidValue(value) {
      return valueRegExp.test(value);
    }

    return {
      isValidLabel: isValidLabel,
      isValidValue: isValidValue
    };
  }());

  /**
   * utility function.
   * use util module if node.js env.
   */
  util = (typeof module !== 'undefined') ? require('util') : (function() {
    /**
     * check value is Array.
     *
     * @param {*} value value.
     * @return {Boolean} return true if value is Array.
     */
    function isArray(value) {
      return (Object.prototype.toString.call(value) === '[object Array]');
    }

    return {
      /** use Array#isArray if implemented. */
      isArray: Array.isArray || isArray
    };
  }());

  /**
   * stream for convert from LTSV to JSON.
   * only implement if node.js env.
   */
  (typeof module !== 'undefined') && (stream = (function() {
    var stream = require('stream'),
        stringDecoder = require('string_decoder').StringDecoder;

    /** inherit from TransformStream */
    util.inherits(LtsvToJsonStream, stream.Transform);

    /**
     * constructor of LtsvToJsonStream.
     *
     * @private
     * @constructor
     * @param {Object} options options object.
     */
    function LtsvToJsonStream(options) {
      options.objectMode = true;
      options.decodeStrings = true;

      stream.Transform.call(this, options);

      this.toObject_ = options.toObject || false;
      this.isStrict_ = options.strict || false;

      this.parseLine_ =
          (this.isStrict_) ? parser.parseLineStrict : parser.parseLine;

      this.buffer_ = '';
      this.splits_ = [];
      this.decoder_ = new stringDecoder(options.encoding || 'utf8');
    }

    /**
     * transform chunk.
     *
     * @param {Buffer|Array} chunk chunk string.
     * @param {String} encoding encoding of chunk.
     * @param {Function} callback callback function.
     */
    LtsvToJsonStream.prototype._transform =
        function(chunk, encoding, callback) {
      var text = this.buffer_ + this.decoder_.write(chunk),
          data = this.splitToLines_(text);

      this.buffer_ = data.tail;
      this.splits_ = data.lines;
      this.append_(this.splits_, callback);
    };

    /**
     * transform end of chunk.
     *
     * @param {Function} callback callback function.
     */
    LtsvToJsonStream.prototype._flush = function(callback) {
      var text = this.buffer_ + this.decoder_.end(),
          data = this.splitToLines_(text);

      // not append if end of line is empty.
      if (data.tail === '') {
        this.splits_ = this.splits_.concat(data.lines);
      } else {
        this.splits_ = this.splits_.concat(data.lines, [data.tail]);
      }

      this.buffer_ = '';
      this.appendAll_(this.splits_, callback);
    };

    /**
     * append records to stream.
     *
     * @private
     * @param {String[]} lines line strings.
     * @param {Function} callback callback function.
     * @return {Undefined} undefined.
     */
    LtsvToJsonStream.prototype.append_ = function(lines, callback) {
      var i, len, line, record;

      try {
        for (i = 0, len = lines.length; i < len; ++i) {
          line = lines.shift();
          record = this.parseLine_(line);

          if (!this.toObject_) {
            record = JSON.stringify(record);
          }

          if (!this.push(record)) {
            lines.unshift(line);
            break;
          }
        }
      } catch (e) {
        return this.emit('error', e);
      }

      (typeof callback === 'function') && callback(null);
    };

    /**
     * append all records to stream.
     *
     * @private
     * @param {String[]} lines line strings.
     * @param {Function} callback callback function.
     */
    LtsvToJsonStream.prototype.appendAll_ = function(lines, callback) {
      this.append_(lines);

      if (lines.length > 0) {
        setImmediate(function(that) {
          that.appendAll_(lines, callback);
        }, this);
      } else {
        this.push(null);
        (typeof callback === 'function') && callback(null);
      }
    };

    /**
     * split to lines from text.
     *
     * @private
     * @param {String} text text string.
     * @return {Object} lines and tail text.
     */
    LtsvToJsonStream.prototype.splitToLines_ = function(text) {
      var curr = 0,
          prev = 0,
          lines = [];

      while ((curr = text.indexOf('\n', prev)) !== -1) {
        lines.push(text.slice(prev, curr));
        prev = curr + 1;
      }

      return {
        lines: lines,
        tail: text.slice(prev)
      };
    };

    /**
     * return new LtsvToJsonStream instance.
     *
     * @param {Object} options option object.
     * @return {LtsvToJsonStream} LtsvToJsonStream instance.
     */
    function createLtsvToJsonStream(options) {
      return new LtsvToJsonStream(options || {});
    }

    return {
      createLtsvToJsonStream: createLtsvToJsonStream
    };
  }()));

  /** if node.js. */
  if (typeof module !== 'undefined') {
    /** export functions for node.js. */
    module.exports = {
      parse: parser.parse,
      parseLine: parser.parseLine,
      parseStrict: parser.parseStrict,
      parseLineStrict: parser.parseLineStrict,
      format: formatter.format,
      formatStrict: formatter.formatStrict,
      createLtsvToJsonStream: stream.createLtsvToJsonStream
    };

    /** export private functions if NODE_ENV variable is test. */
    if (process.env.NODE_ENV === 'test') {
      module.exports.parser_ = parser;
      module.exports.formatter_ = formatter;
      module.exports.validator_ = validator;
      module.exports.util_ = util;
    }
  } else {
    /** export functions for browser. */
    global.ltsv = {
      parse: parser.parse,
      parseLine: parser.parseLine,
      parseStrict: parser.parseStrict,
      parseLineStrict: parser.parseLineStrict,
      format: formatter.format,
      formatStrict: formatter.formatStrict
    };

    /** export private functions if declared Mocha. */
    if (typeof Mocha !== 'undefined') {
      global.ltsv.parser_ = parser;
      global.ltsv.formatter_ = formatter;
      global.ltsv.validator_ = validator;
      global.ltsv.util_ = util;
    }
  }

}(this));
<|endoftext|>


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

  var uiSelectModule = angular.module('fng.uiSelect', ['ui.select']);

  uiSelectModule.factory('uiSelectHelper', ['$rootScope', '$q', 'SubmissionsService', function ($rootScope, $q, SubmissionsService) {
    var lastW, lastH;
    var localLookups = {};
    return {
      windowChanged: function (w, h) {
        var result = false;
        if (w !== lastW || h !== lastH) {
          lastW = w;
          lastH = h;
          result = true;
        }
        return result;
      },
      addClientLookup: function (lkpName, lkpData) {
        localLookups[lkpName] = lkpData;
      },
      lookupFunc: function (value, formSchema, cb) {
        if (formSchema.array) {
          // TODO extend back end to do multiple lookups in one hit

          var promises = [];
          var results = [];
          angular.forEach(value, function (obj) {
            promises.push(SubmissionsService.getListAttributes(formSchema.ref, obj.x));
          });
          $q.all(promises).then(function (responses) {
            angular.forEach(responses, function (response) {
              results.push({x: {id: value.shift().x, text: response.data.list}});
            });
            cb(formSchema, results);
            setTimeout(function () {
              $rootScope.$digest();
            });
          });
        } else if (formSchema.fngUiSelect.deriveOptions) {
          var retVal;
          if (typeof value === 'string') {
            var obj = localLookups[formSchema.fngUiSelect.deriveOptions].find(function (test) {
              return test.id == value
            });
            retVal = {id: value, text: obj ? obj.text : ''}
          } else {
            retVal = value;
          }
          cb(formSchema, retVal);
        } else {
          SubmissionsService.getListAttributes(formSchema.ref, value).then(function (response) {
            cb(formSchema, {id: value, text: response.data.list});
          });
        }
      }
    };
  }]);

  uiSelectModule.controller('FngUISelectCtrl', ['$scope', '$window', '$timeout', '$http', 'uiSelectHelper', function ($scope, $window, $timeout, $http, uiSelectHelper) {

    $scope.uiSelect = $scope.uiSelect || [];

    $scope.windowResizeUiSelect = function (checkResize) {
      // This is to stop the resize firing too often.  Really horrible way of doing it caused by some very regrettable design choices which will be factored out later
      // Actually not choices - just not knowing any better
      if (!checkResize || uiSelectHelper.windowChanged($window.innerWidth, $window.innerHeight)) {
        for (var i = 0; i < $scope.uiSelect.length; i++) {
          var selectId = $scope.uiSelect[i].selectId;
          var select = document.getElementById(selectId);
          if (select && select.offsetParent) {
            var helper = document.getElementById(selectId + '_width-helper');
            if (helper) {
              var w = helper.offsetWidth;
              if (select.offsetLeft + w > $window.innerWidth) {
                w = $window.innerWidth - select.offsetLeft - 20;
              }
              angular.element(document.getElementById(selectId)).css('width', w);
            } else {
              console.log('No helper element found for ' + selectId);
            }
          }
        }
      }
    };

    $scope.refreshOptions = function (searchString, id) {
      var elem = _.find($scope.uiSelect, function (elem) {
        return elem.selectId === id
      });
      if (elem) {
        if (searchString.length === 0) {
          $scope[id + '_options'] = [];
        } else {
          var promise;
          if (elem.filter && elem.filter[0] === '/') {
            // HACK We want to pass the existing record in e, but we can fall foul of 431 errors if it is large, so we test for that
            // using an arbitrary number and if so just send the _id (on the assumption that large records won't be new so will have an _id).
            // According to https://stackoverflow.com/questions/57431355/how-to-fix-431-request-header-fields-too-large-in-react-redux-app the limit is 8KB
            // but I have seen it fail at about 5k...
            var record = JSON.stringify($scope.record).length >= (4 * 1024) ? {_id: $scope.record._id} : $scope.record;
            // Custom URL
            promise = $http({
              method: 'GET',
              url: elem.filter,
              params: {
                q: searchString,
                e: record,
                i: $scope.$index
              },
              cache: false
            });
          } else {
            promise = $http.get('/api/search/' + elem.ref + '?q=' + searchString + (elem.filter ? ('&f=' + elem.filter) : ''));
          }
          promise.then(function (response) {
            if (response.status >= 400) {
              throw new Error(response.statusMessage)
            } else {
              if (elem.additional) {
                $scope[id + '_options'] = response.data.results.map(function (result) {
                  result.text = result.text + (result.additional ? (', ' + result.additional) : '');
                  return result;
                });
              } else {
                $scope[id + '_options'] = response.data.results;
              }
            }
          })
          .catch(function(err) {
            var msg;
            if (err && err.data && err.data.message) {
              msg = err.data.message;
            } else {
              msg = 'Error ' + err.status + ': ' + err.statusText + ' - ' + err.data;
            }
            $scope.showError(msg);
          });
        }
      } else {
        throw new Error('Could not find uiSelect element for ' + id);
      }
    };

    angular.element($window).bind('resize', function () {
      $scope.windowResizeUiSelect(true);
      return $scope.$apply();
    });

    $timeout($scope.windowResizeUiSelect);
  }]);

  uiSelectModule.directive('fngUiSelect', ['$compile', '$window', 'pluginHelper', 'cssFrameworkService', 'formMarkupHelper', 'uiSelectHelper',
    function ($compile, $window, pluginHelper, cssFrameworkService, formMarkupHelper, uiSelectHelper) {
      return {
        restrict: 'E',
        controller: 'FngUISelectCtrl',
        link: function (scope, element, attr) {

          function addToConversions(path, options) {
            if (Object.keys(options).length > 0) {
              var keys = path.split('.');
              if (!scope.conversions) {
                  scope.conversions = {};
              }
              var target = scope.conversions;
              for (var i = 0; i < keys.length; i++) {
                var thisKey = keys[i];
                target[thisKey] = target[thisKey] || {};
                target = target[thisKey];
              }
              angular.extend(target, options);
            }
          }

          var processedAttr = pluginHelper.extractFromAttr(attr, 'fngUiSelect');
          var elemScope = angular.extend({selectId: processedAttr.info.id}, processedAttr.directiveOptions);
          var multi = processedAttr.info.array;
          var elementHtml;
          var input = '';

          scope.uiSelect.push(elemScope);
          addToConversions(processedAttr.info.name, processedAttr.directiveOptions);

          // Sort out the theme, defaulting to select2 (so old users won't see the change).  Bootstrap theme only works with Bootstrap 3
          // and not with animate (see https://github.com/angular-ui/ui-select/issues/1731 and others)
          var theme = processedAttr.directiveOptions.theme || 'select2';
          if (theme === 'bootstrap') {
            if (cssFrameworkService.framework() !== 'bs3') {
              theme = 'select2';
            } else {
              try {
                if (angular.module('ngAnimate')) {
                  theme = 'select2';
                }
              } catch(e) {
                ;    // module not present, so bootstrap theme will work
              }
            }
          }

          var multiControl = false;
          var multiStr = '';
          if (multi && (processedAttr.directiveOptions.fngajax || processedAttr.directiveOptions.forcemultiple)) {
            // We need the array to be an array of objects with a x property.  This tells forms-angular to convert it by
            // adding an attribute to the schema.
            pluginHelper.findIdInSchemaAndFlagNeedX(scope.baseSchema(), processedAttr.info.id);
            multiControl = true;
          } else {
            multiStr = multi ? 'multiple close-on-select reset-search-input ' : '';
          }

          var requiredStr = '';
          var allowClearStr = '';
          if (processedAttr.info.required) {
            requiredStr = ' ng-required="true"';
          } else {
            allowClearStr = ' allow-clear';
          }
          var disabledStr = '';
          if (processedAttr.directiveOptions.ngdisabled) {
            disabledStr = ` ng-disabled="${processedAttr.directiveOptions.ngdisabled}"`
          } else {
            disabledStr = ' ng-disabled="disabled"';
          }

          // First of all add a hidden input field which we will use to set the width of the select
          if (!angular.element('#' + processedAttr.info.id + '_width-helper').length > 0) {
            var hiddenInputInfo = {
              id: processedAttr.info.id + '_width-helper',
              name: processedAttr.info.name + '_width-helper',
              label: ''
            };
            if (processedAttr.info.size) {
              hiddenInputInfo.size = processedAttr.info.size;
            }
            input = pluginHelper.buildInputMarkup(scope, attr.model, hiddenInputInfo, processedAttr.options, false, multiControl, function (buildingBlocks) {
              return '<input id="' + hiddenInputInfo.id + '" type="text" class="form-control" disabled="" aria-label="unused input" style="position: absolute; left: -4200px;">';
            });
            input = input.replace('class="row', 'class="hidden-row row')
          }

          function optionsFromArray(multiControl, multi, array, arrayGetter) {
            var isObjects = scope[array] && (scope[array].isObjects || typeof scope[array][0] === "object");
            if (isObjects) {
              addToConversions(processedAttr.info.name, {fngajax: uiSelectHelper.lookupFunc});
              uiSelectHelper.addClientLookup(arrayGetter, scope[array]);
            }
            var select = '';
            if (multiControl) {
              select += '{{$select.selected' + (isObjects ? '.text' : '') + '}}';
            } else {
              select += multi ? '{{$item}}' : ('{{$select.selected' + (isObjects ? '.text' : '') + '}}');
            }
            select += '</ui-select-match>';
            select += '<ui-select-choices repeat="option in ' + array + ' | filter:$select.search">';
            select += '

';
            return select;
          }

          elementHtml = pluginHelper.buildInputMarkup(scope, attr.model, processedAttr.info, processedAttr.options, multiControl, multiControl, function (buildingBlocks) {
            var defaultPlaceholder = 'Select an option...';
            if (processedAttr.directiveOptions.fngajax) {
              defaultPlaceholder = 'Start typing...'
            }
            // set up the ui-select directives
            var select = '<ui-select ' + multiStr + buildingBlocks.common + requiredStr + disabledStr + ' theme="' + theme + '" style="min-width:18em;">'
            select += '<ui-select-match' + allowClearStr + ' placeholder="' + (processedAttr.info.placeholder || defaultPlaceholder) + '">';

            if (processedAttr.directiveOptions.fngajax) {
              // Stash any filters
              if (processedAttr.directiveOptions.fngajax !== true) {
                elemScope.filter = processedAttr.directiveOptions.fngajax;
              }
              // Set up lookup function
              addToConversions(processedAttr.info.name, {fngajax: uiSelectHelper.lookupFunc});
              // Use the forms-angular API to query the referenced collection
              elemScope.ref = processedAttr.info.ref;
              scope[processedAttr.info.id + '_options'] = [];
              if (multiControl) {
                select += '{{$select.selected.text}}';
              } else if (processedAttr.options.subschema) {
                select += '{{' + attr.model + '.' + processedAttr.info.name.replace(processedAttr.options.subschemaroot, processedAttr.options.subschemaroot + '[$index]') + '.text}}';
              } else {
                select += '{{' + buildingBlocks.modelString + '.text}}';
              }
              select += '</ui-select-match>';
              select += '<ui-select-choices repeat="option in (' + processedAttr.info.id + '_options) track by $index" ';
              select += 'refresh="refreshOptions($select.search, \'' + processedAttr.info.id + '\')" ';
              select += 'refresh-delay="100"> ';
              select += '

';
            } else if (processedAttr.directiveOptions.deriveoptions) {
              if (typeof scope[processedAttr.directiveOptions.deriveoptions] === "function") {
                select += optionsFromArray(multiControl, multi, scope[processedAttr.directiveOptions.deriveoptions](), processedAttr.directiveOptions.deriveoptions);
              } else {
                throw new Error("In fng-ui-select " + processedAttr.directiveOptions.deriveoptions + " is not a function on the scope");
              }
            } else if (processedAttr.info.options) {
              // Simple case - enumerated options on the form scope
              select += optionsFromArray(multiControl, multi, processedAttr.info.options);
            } else {
              throw new Error('fng-ui-select has no means of populating select');
            }
            select += '</ui-select-choices>';
            select += '</ui-select>';
            return select;
          });
          element.append($compile(input + elementHtml)(scope));
        }
      }
    }]);
})();
<commit_msg>Properly support readonly and fng security<commit_after>(function () {
  'use strict';

  var uiSelectModule = angular.module('fng.uiSelect', ['ui.select']);

  uiSelectModule.factory('uiSelectHelper', ['$rootScope', '$q', 'SubmissionsService', function ($rootScope, $q, SubmissionsService) {
    var lastW, lastH;
    var localLookups = {};
    return {
      windowChanged: function (w, h) {
        var result = false;
        if (w !== lastW || h !== lastH) {
          lastW = w;
          lastH = h;
          result = true;
        }
        return result;
      },
      addClientLookup: function (lkpName, lkpData) {
        localLookups[lkpName] = lkpData;
      },
      lookupFunc: function (value, formSchema, cb) {
        if (formSchema.array) {
          // TODO extend back end to do multiple lookups in one hit

          var promises = [];
          var results = [];
          angular.forEach(value, function (obj) {
            promises.push(SubmissionsService.getListAttributes(formSchema.ref, obj.x));
          });
          $q.all(promises).then(function (responses) {
            angular.forEach(responses, function (response) {
              results.push({x: {id: value.shift().x, text: response.data.list}});
            });
            cb(formSchema, results);
            setTimeout(function () {
              $rootScope.$digest();
            });
          });
        } else if (formSchema.fngUiSelect.deriveOptions) {
          var retVal;
          if (typeof value === 'string') {
            var obj = localLookups[formSchema.fngUiSelect.deriveOptions].find(function (test) {
              return test.id == value
            });
            retVal = {id: value, text: obj ? obj.text : ''}
          } else {
            retVal = value;
          }
          cb(formSchema, retVal);
        } else {
          SubmissionsService.getListAttributes(formSchema.ref, value).then(function (response) {
            cb(formSchema, {id: value, text: response.data.list});
          });
        }
      }
    };
  }]);

  uiSelectModule.controller('FngUISelectCtrl', ['$scope', '$window', '$timeout', '$http', 'uiSelectHelper', function ($scope, $window, $timeout, $http, uiSelectHelper) {

    $scope.uiSelect = $scope.uiSelect || [];

    $scope.windowResizeUiSelect = function (checkResize) {
      // This is to stop the resize firing too often.  Really horrible way of doing it caused by some very regrettable design choices which will be factored out later
      // Actually not choices - just not knowing any better
      if (!checkResize || uiSelectHelper.windowChanged($window.innerWidth, $window.innerHeight)) {
        for (var i = 0; i < $scope.uiSelect.length; i++) {
          var selectId = $scope.uiSelect[i].selectId;
          var select = document.getElementById(selectId);
          if (select && select.offsetParent) {
            var helper = document.getElementById(selectId + '_width-helper');
            if (helper) {
              var w = helper.offsetWidth;
              if (select.offsetLeft + w > $window.innerWidth) {
                w = $window.innerWidth - select.offsetLeft - 20;
              }
              angular.element(document.getElementById(selectId)).css('width', w);
            } else {
              console.log('No helper element found for ' + selectId);
            }
          }
        }
      }
    };

    $scope.refreshOptions = function (searchString, id) {
      var elem = _.find($scope.uiSelect, function (elem) {
        return elem.selectId === id
      });
      if (elem) {
        if (searchString.length === 0) {
          $scope[id + '_options'] = [];
        } else {
          var promise;
          if (elem.filter && elem.filter[0] === '/') {
            // HACK We want to pass the existing record in e, but we can fall foul of 431 errors if it is large, so we test for that
            // using an arbitrary number and if so just send the _id (on the assumption that large records won't be new so will have an _id).
            // According to https://stackoverflow.com/questions/57431355/how-to-fix-431-request-header-fields-too-large-in-react-redux-app the limit is 8KB
            // but I have seen it fail at about 5k...
            var record = JSON.stringify($scope.record).length >= (4 * 1024) ? {_id: $scope.record._id} : $scope.record;
            // Custom URL
            promise = $http({
              method: 'GET',
              url: elem.filter,
              params: {
                q: searchString,
                e: record,
                i: $scope.$index
              },
              cache: false
            });
          } else {
            promise = $http.get('/api/search/' + elem.ref + '?q=' + searchString + (elem.filter ? ('&f=' + elem.filter) : ''));
          }
          promise.then(function (response) {
            if (response.status >= 400) {
              throw new Error(response.statusMessage)
            } else {
              if (elem.additional) {
                $scope[id + '_options'] = response.data.results.map(function (result) {
                  result.text = result.text + (result.additional ? (', ' + result.additional) : '');
                  return result;
                });
              } else {
                $scope[id + '_options'] = response.data.results;
              }
            }
          })
          .catch(function(err) {
            var msg;
            if (err && err.data && err.data.message) {
              msg = err.data.message;
            } else {
              msg = 'Error ' + err.status + ': ' + err.statusText + ' - ' + err.data;
            }
            $scope.showError(msg);
          });
        }
      } else {
        throw new Error('Could not find uiSelect element for ' + id);
      }
    };

    angular.element($window).bind('resize', function () {
      $scope.windowResizeUiSelect(true);
      return $scope.$apply();
    });

    $timeout($scope.windowResizeUiSelect);
  }]);

  uiSelectModule.directive('fngUiSelect', ['$compile', '$window', 'pluginHelper', 'cssFrameworkService', 'formMarkupHelper', 'uiSelectHelper',
    function ($compile, $window, pluginHelper, cssFrameworkService, formMarkupHelper, uiSelectHelper) {
      return {
        restrict: 'E',
        controller: 'FngUISelectCtrl',
        link: function (scope, element, attr) {

          function addToConversions(path, options) {
            if (Object.keys(options).length > 0) {
              var keys = path.split('.');
              if (!scope.conversions) {
                  scope.conversions = {};
              }
              var target = scope.conversions;
              for (var i = 0; i < keys.length; i++) {
                var thisKey = keys[i];
                target[thisKey] = target[thisKey] || {};
                target = target[thisKey];
              }
              angular.extend(target, options);
            }
          }

          var processedAttr = pluginHelper.extractFromAttr(attr, 'fngUiSelect');
          const id = processedAttr.info.id;
          var elemScope = angular.extend({selectId: id}, processedAttr.directiveOptions);
          var multi = processedAttr.info.array;
          var elementHtml;
          var input = '';

          scope.uiSelect.push(elemScope);
          addToConversions(processedAttr.info.name, processedAttr.directiveOptions);

          // Sort out the theme, defaulting to select2 (so old users won't see the change).  Bootstrap theme only works with Bootstrap 3
          // and not with animate (see https://github.com/angular-ui/ui-select/issues/1731 and others)
          var theme = processedAttr.directiveOptions.theme || 'select2';
          if (theme === 'bootstrap') {
            if (cssFrameworkService.framework() !== 'bs3') {
              theme = 'select2';
            } else {
              try {
                if (angular.module('ngAnimate')) {
                  theme = 'select2';
                }
              } catch(e) {
                ;    // module not present, so bootstrap theme will work
              }
            }
          }

          var multiControl = false;
          var multiStr = '';
          if (multi && (processedAttr.directiveOptions.fngajax || processedAttr.directiveOptions.forcemultiple)) {
            // We need the array to be an array of objects with a x property.  This tells forms-angular to convert it by
            // adding an attribute to the schema.
            pluginHelper.findIdInSchemaAndFlagNeedX(scope.baseSchema(), id);
            multiControl = true;
          } else {
            multiStr = multi ? 'multiple close-on-select reset-search-input ' : '';
          }

          var requiredStr = '';
          var allowClearStr = '';
          if (processedAttr.info.required) {
            requiredStr = ' ng-required="true"';
          } else {
            allowClearStr = ' allow-clear';
          }
          const disabledStr = pluginHelper.handleReadOnlyDisabled(id, attr);

          // First of all add a hidden input field which we will use to set the width of the select
          if (!angular.element(`#${id}_width-helper`).length > 0) {
            var hiddenInputInfo = {
              id: `${id}_width-helper`,
              name: processedAttr.info.name + '_width-helper',
              label: ''
            };
            if (processedAttr.info.size) {
              hiddenInputInfo.size = processedAttr.info.size;
            }
            input = pluginHelper.buildInputMarkup(scope, attr.model, hiddenInputInfo, processedAttr.options, false, multiControl, function (buildingBlocks) {
              return '<input id="' + hiddenInputInfo.id + '" type="text" class="form-control" disabled="" aria-label="unused input" style="position: absolute; left: -4200px;">';
            });
            input = input.replace('class="row', 'class="hidden-row row')
          }

          function optionsFromArray(multiControl, multi, array, arrayGetter) {
            var isObjects = scope[array] && (scope[array].isObjects || typeof scope[array][0] === "object");
            if (isObjects) {
              addToConversions(processedAttr.info.name, {fngajax: uiSelectHelper.lookupFunc});
              uiSelectHelper.addClientLookup(arrayGetter, scope[array]);
            }
            var select = '';
            if (multiControl) {
              select += '{{$select.selected' + (isObjects ? '.text' : '') + '}}';
            } else {
              select += multi ? '{{$item}}' : ('{{$select.selected' + (isObjects ? '.text' : '') + '}}');
            }
            select += '</ui-select-match>';
            select += '<ui-select-choices repeat="option in ' + array + ' | filter:$select.search">';
            select += '

';
            return select;
          }

          elementHtml = pluginHelper.buildInputMarkup(scope, attr.model, processedAttr.info, processedAttr.options, multiControl, multiControl, function (buildingBlocks) {
            var defaultPlaceholder = 'Select an option...';
            if (processedAttr.directiveOptions.fngajax) {
              defaultPlaceholder = 'Start typing...'
            }
            // set up the ui-select directives
            var select = '<ui-select ' + multiStr + buildingBlocks.common + requiredStr + disabledStr + ' theme="' + theme + '" style="min-width:18em;">'
            select += '<ui-select-match' + allowClearStr + ' placeholder="' + (processedAttr.info.placeholder || defaultPlaceholder) + '">';

            if (processedAttr.directiveOptions.fngajax) {
              // Stash any filters
              if (processedAttr.directiveOptions.fngajax !== true) {
                elemScope.filter = processedAttr.directiveOptions.fngajax;
              }
              // Set up lookup function
              addToConversions(processedAttr.info.name, {fngajax: uiSelectHelper.lookupFunc});
              // Use the forms-angular API to query the referenced collection
              elemScope.ref = processedAttr.info.ref;
              scope[`${id}_options`] = [];
              if (multiControl) {
                select += '{{$select.selected.text}}';
              } else if (processedAttr.options.subschema) {
                select += '{{' + attr.model + '.' + processedAttr.info.name.replace(processedAttr.options.subschemaroot, processedAttr.options.subschemaroot + '[$index]') + '.text}}';
              } else {
                select += '{{' + buildingBlocks.modelString + '.text}}';
              }
              select += '</ui-select-match>';
              select += `<ui-select-choices repeat="option in (${id}_options) track by $index" `;
              select += `refresh="refreshOptions($select.search, '${id}')" `;
              select += 'refresh-delay="100"> ';
              select += '

';
            } else if (processedAttr.directiveOptions.deriveoptions) {
              if (typeof scope[processedAttr.directiveOptions.deriveoptions] === "function") {
                select += optionsFromArray(multiControl, multi, scope[processedAttr.directiveOptions.deriveoptions](), processedAttr.directiveOptions.deriveoptions);
              } else {
                throw new Error("In fng-ui-select " + processedAttr.directiveOptions.deriveoptions + " is not a function on the scope");
              }
            } else if (processedAttr.info.options) {
              // Simple case - enumerated options on the form scope
              select += optionsFromArray(multiControl, multi, processedAttr.info.options);
            } else {
              throw new Error('fng-ui-select has no means of populating select');
            }
            select += '</ui-select-choices>';
            select += '</ui-select>';
            return select;
          });
          element.append($compile(input + elementHtml)(scope));
        }
      }
    }]);
})();
<|endoftext|>


<commit_before>var _ = require('lodash'),
  Kramed = require('./kramed'),
  Renderer = new Kramed.Renderer();

// Parser Opts
Kramed.setOptions({
  renderer: Renderer,
  gfm: true,
  tables: true,
  breaks: false,
  pedantic: false,
  sanitize: true,
  smartLists: true,
  smartypants: false
});

/**
 * Encode non-alpha string - HTML
 * return: string
 *
*/
function HTMLEncode(str){
  var i = str.length,
      aRet = [];

  while (i--) {
    var iC = str[i].charCodeAt();
    if (iC < 65 || iC > 127 || (iC>90 && iC<97)) {
      aRet[i] = '&#'+iC+';';
    } else {
      aRet[i] = str[i];
    }
   }
  return aRet.join('');
}

;(function() {

/**
 * Markdown to Shower
 * return: string
 *
*/

function Pony(options) {

  var self = this;

  var options = options || {};

  self.debug = options.debug||false;

  self.version = require('../package.json').version;

  self.heading = (options.heading) ? options.heading : [];
  self.sections = (options.sections) ? options.sections : 0;
  self.hideSlideNumbers = (options.hideSlideNumbers) ? options.hideSlideNumbers : "";
  self.hrc = (options.hrc) ? options.hrc : [];

  self.error = (options.error) ? options.error : "

Rendering error

";

  self.header = options.header;
  self.footer = options.footer;

  self._theme = false;
  self.isPonyFile = false;

  Renderer.ponyoptions = {
    format: "",
    slidenumbers: true,
    footer: 'Pony Show',
    timing:false,
    progress:true,
    transition:false,
    shadows:false
  };

  Renderer.list = function(body, ordered) {
    var type = ordered ? 'ol' : 'ul', out="", cls="";

    out = '<' + type + '>\n' + body + '</' + type + '>\n';

    if (self.sections > 0 || !self.isPonyFile) {
      return out;
    } else {
      self.heading.push(out);
      return '';
    }
  };

  Renderer.listitem = function(text) {
    var cls="";
    if (Renderer.ponyoptions.buildLists) {
      cls = "next";
    }

    return '
' + text + '
\n';
  }

  Renderer.hr = function(cls,id) {
    var out = null;
    var timing = false;

    // set format for each section
    if (Renderer.ponyoptions.format) {
      cls = Renderer.ponyoptions.format + " " + cls;
    }

    // Set timing option
    if (Renderer.ponyoptions.timing) {
      timing = 'data-timing="'+Renderer.ponyoptions.timing+'"';
    }

    if (self.sections == 0) {

      // heading
      if (self.heading.length > 0) {
        out = self.heading.join('') + "\n";
      }

      out = out+"\n
\n
\n";
    } else {

      if (self.sections == self.hrc.length) {
        out = "\n
\n
\n";
      } else {
        out = "
\n
\n\n
\n
\n";
      }
    }

    self.sections++;

    return out||"";
  }

  Renderer.heading = function (text, level) {
    var out = false;

    // var str = text.split('[');
    // var style = null;
    //
    // switch(str[0]) {
    //   case 'absolute':
    //     str.shift();
    //     style = str.join('');
    //     break;
    //
    //   default:
    // }

    if (self.sections == 0 && self.heading.length == 0 && self.isPonyFile) {
      self.heading.push('\n');
      self.heading.push('<h' + level + '>' + text + '</h' + level + '>\n');
    } else if (self.sections == 0 && self.isPonyFile && (self.heading.length > 1)) {
      self.heading.push('<h' + level + '>' + text + '</h' + level + '>\n');
    } else {
      // default
      out = '<h' + level + ' class="fit">' + text + '</h' + level + '>\n';
    }

    return (out) ? out : "";
  };

  Renderer.paragraph = function(text) {
    var out = null;
    var arr = [];

    text = text.trim();

    // ignore empty text
    if (text.length == 0) {
      return "";
    }

    if (self.sections > 0 || !self.isPonyFile) {

      // add line breaks
      if (text.indexOf("\n") > -1) {
        arr = text.split("\n");
      } else {
        arr.push(text);
      }

      for (x in arr) {
        var img = text.indexOf(" -1) ? "" : "
";
        post = (img > -1) ? "\n" : "
\n";
      }

      out = pre+arr.join('\n\n')+post;

    } else {

      // Add to heading and ignore all other text
      if (self.sections == 0 && self.heading.length > 0) {
        self.heading.push("
"+text+"
\n");
      }

    }
    return out||"";
  }

	function 	parseClass(text) {

		var str = text.split(' ');
    var cls = "";
    var style = null;
    var pos = null;

    switch(str[0]) {

      case 'fullscreen':
        cls = 'fullscreen';
        break;

      case 'fit':
        cls = "fit";
        break;

      case 'left':
        cls = "left";
				str.shift();
        style = str.join('');
        break;

      case 'right':
        cls = "right";
				str.shift();
        style = str.join('');
        break;

      case 'top':
				cls = "top";
				str.shift();
				style = str.join('');
				break;

		  case 'bottom':
				cls = "bottom";
				str.shift();
				style = str.join('');
				break;

	    case 'css':
	      str.shift();
	      style = str.join(' ');
	      break;

      default:
        cls = "";
    }

		if (Renderer.ponyoptions.shadows) {
      cls = cls+" shadow";
    }

		return {
			cls: cls,
			style: style
		}

	}

  // images are rendered as paragraph / text
  Renderer.image = function(href, title, text) {

    var out = null;

		var attr = parseClass(text);

    var out = '' : '>';
    return out;

  }

  Renderer.link = function(href, title, text) {
    if (this.options.sanitize) {
      try {
        var prot = decodeURIComponent(unescape(href))
          .replace(/[^\w:]/g, '')
          .toLowerCase();
      } catch (e) {
        return '';
      }
      if (prot.indexOf('javascript:') === 0) {
        return '';
      }
    }
    var out = '' + text + '';
    return out;
  };

  Renderer.code = function(text,lang) {
		var attr = {};

		// parse style attr if available
		if (lang.indexOf(']') >= 0) {
			lang = lang.split(']');
			attr = lang[0].replace('[','');
			lang = lang[1];
			attr = parseClass(attr);
		}

		// console.log('cls: '+attr.cls);
		// console.log('style: '+attr.style);
		// console.log('lang: '+lang);

		switch(lang) {
			case 'xml':
				text = HTMLEncode(text);
				break;
			case 'markup':
				text = HTMLEncode(text);
				break;
		}

		var out = '
";

    return out+''+text+'
';
  }

  Renderer.table = function(header, body) {

    return '
\n'
      + '
\n'
      + header
      + '
\n'
      + '
\n'
      + body
      + '
\n'
      + '
\n';
  }

}

Pony.prototype.setHeader = function(str){
  this.header = str;
}

Pony.prototype.setFooter = function(str){
  this.footer = str;
}

Pony.prototype.setDebug = function(bool) {
  this.debug = bool;
}

Pony.prototype.log = function(args) {
  if (this.debug) {
    console.log(args);
  }
}

Pony.prototype.render = function(dat,opts) {

  var self = this;

  var header, footer, LEXED, htmlClass;

  // Set options (if avail)
  var _globalopts = opts||{};

  // Reset
  this.heading = [];
  this.sections = 0;
  this.hideSlideNumbers = "";
  this.hrc = [];

  var tokens = Kramed.lexer(dat);

  // Check if first token is configuration
  if (tokens[0].type == "paragraph") {
    if (tokens[0].text.indexOf("\n") > -1) {

      self.isPonyFile = true;

      // rebase tokens
      var rebase = tokens.shift();

      var arr = rebase.text.split("\n");

      // Parse options
      for(var x=0;x<arr.length;x++) {

        opts = arr[x].split(":");

        var _key = opts[0].trim().toLowerCase();
        var _prop = opts[1].trim().toLowerCase();

        self.log(_key + " = " + _prop);

        switch(_key) {

          case 'theme':
            Renderer.ponyoptions.theme = _prop;
            break;

          case 'footer':
            Renderer.ponyoptions.footer = _prop;
            break;

          case 'slidenumbers':
            Renderer.ponyoptions.slidenumbers = (_prop == "false") ? false : true;
            break;

          case 'timing':
            Renderer.ponyoptions.timing = _prop;
            break;

          case 'error':
            self.error = opts[1].trim();
            break;

          case 'tableclass':
            Renderer.ponyoptions.tableclass = _prop||"";
            break;

          case 'format':
            Renderer.ponyoptions.format = _prop||"";
            break;

          case 'progress':
            Renderer.ponyoptions.progress = (_prop == "false") ? false : true;
            break;

          case 'build-lists':
            Renderer.ponyoptions.buildLists = (_prop == "false") ? false : true;
            break;

          case 'transition':
            Renderer.ponyoptions.transition = _prop;
            break;

          case 'shadows':
            Renderer.ponyoptions.shadows = (_prop == "false") ? false : true;
            break;

          default:
            //out = "
"+text+"
";
        }

      }

    }
  }

  self.log(tokens);

  // count HR
  for(var x in tokens) {
    if (tokens[x].type == "hr") {
      self.hrc.push(x);
    }
  }

  if (tokens[self.hrc[self.hrc.length-1]]) {
    tokens[self.hrc[self.hrc.length-1]].last = true;
  }

  // Check if ponyfile
  if (!self.isPonyFile) {
    for(var x=0;x<tokens.length;x++) {
      if (tokens[x].type == "paragraph") {
        if ((tokens[x].text.indexOf("\n---\n") > -1)) {
          self.isPonyFile = true;
        }
      }
    }
  }

  LEXED = Kramed.parser(tokens);

  self.log(LEXED);
  self.log('___________');

  if (LEXED.length == 0) {
    LEXED = self.error;
  }

  // Merge file config with global configs giving preference to file config
  Renderer.ponyoptions = _.assign(_globalopts, Renderer.ponyoptions);

  if (!Renderer.ponyoptions.slidenumbers) {
    self.hideSlideNumbers = '';
  }

  // Apply transition
  if (Renderer.ponyoptions.transition) {
    htmlClass = Renderer.ponyoptions.transition;

    // update defined header
    if (self.header) {
      self.header = self.header.replace(/().*?<head/mgi, '$1 class="'+htmlClass+'"');
    }
  }

  header = self.header||'<!DOCTYPE HTML>' +
  '\n' +
  '\n' +
  	'<title>Pony Show IDE</title>\n' +
  	'<meta charset="utf-8">\n' +
  	'<meta name="viewport" content="width=792, user-scalable=no">\n' +
  	'<meta http-equiv="x-ua-compatible" content="ie=edge">\n' +
  	'<link rel="stylesheet" href="css/main.css">\n' +
  	'<link rel="stylesheet" href="themes/'+Renderer.ponyoptions.theme+'/styles/screen.css">\n' +
  	self.hideSlideNumbers +
  '\n' +
  '\n\n';

  footer = self.footer||'\n' +
  '\n' +
  '';

  if (Renderer.ponyoptions.progress) {
    footer = '\n

\n' + footer;
  }

  // Check if last section.
  if (self.sections == self.hrc.length) {
    LEXED = LEXED+"\n
\n
\n";
  }

  return header+LEXED+footer;

};

if (typeof module !== 'undefined' && typeof exports === 'object') {
  module.exports = new Pony;
} else if (typeof define === 'function' && define.amd) {
  define(function() { return Pony; });
} else {
  this.render = new Pony;
}

}).call(function() {
  return this || (typeof window !== 'undefined' ? window : global);
}());<commit_msg>add bracket styling to blockquotes<commit_after>var _ = require('lodash'),
  Kramed = require('./kramed'),
  Renderer = new Kramed.Renderer();

// Parser Opts
Kramed.setOptions({
  renderer: Renderer,
  gfm: true,
  tables: true,
  breaks: false,
  pedantic: false,
  sanitize: true,
  smartLists: true,
  smartypants: false
});

/**
 * Encode non-alpha string - HTML
 * return: string
 *
*/
function HTMLEncode(str){
  var i = str.length,
      aRet = [];

  while (i--) {
    var iC = str[i].charCodeAt();
    if (iC < 65 || iC > 127 || (iC>90 && iC<97)) {
      aRet[i] = '&#'+iC+';';
    } else {
      aRet[i] = str[i];
    }
   }
  return aRet.join('');
}

function parseClass(text) {

	var str = text.split(' ');
  var cls = "";
  var style = null;
  var pos = null;

  switch(str[0]) {

    case 'fullscreen':
      cls = 'fullscreen';
      break;

    case 'fit':
      cls = "fit";
      break;

    case 'left':
      cls = "left";
			str.shift();
      style = str.join('');
      break;

    case 'right':
      cls = "right";
			str.shift();
      style = str.join('');
      break;

    case 'top':
			cls = "top";
			str.shift();
			style = str.join('');
			break;

	  case 'bottom':
			cls = "bottom";
			str.shift();
			style = str.join('');
			break;

    case 'css':
      str.shift();
      style = str.join(' ');
      break;

    default:
      cls = str[0];
  }

	if (Renderer.ponyoptions.shadows) {
    cls = cls+" shadow";
  }

	return {
		cls: cls,
		style: style
	}

}

;(function() {

/**
 * Markdown to Shower
 * return: string
 *
*/

function Pony(options) {

  var self = this;

  var options = options || {};

  self.debug = options.debug||false;

  self.version = require('../package.json').version;

  self.heading = (options.heading) ? options.heading : [];
  self.sections = (options.sections) ? options.sections : 0;
  self.hideSlideNumbers = (options.hideSlideNumbers) ? options.hideSlideNumbers : "";
  self.hrc = (options.hrc) ? options.hrc : [];

  self.error = (options.error) ? options.error : "

Rendering error

";

  self.header = options.header;
  self.footer = options.footer;

  self._theme = false;
  self.isPonyFile = false;

  Renderer.ponyoptions = {
    format: "",
    slidenumbers: true,
    footer: 'Pony Show',
    timing:false,
    progress:true,
    transition:false,
    shadows:false
  };

  Renderer.list = function(body, ordered) {
    var type = ordered ? 'ol' : 'ul', out="", cls="";

    out = '<' + type + '>\n' + body + '</' + type + '>\n';

    if (self.sections > 0 || !self.isPonyFile) {
      return out;
    } else {
      self.heading.push(out);
      return '';
    }
  };

  Renderer.listitem = function(text) {
    var cls="";
    if (Renderer.ponyoptions.buildLists) {
      cls = "next";
    }

    return '
' + text + '
\n';
  }

  Renderer.hr = function(cls,id) {
    var out = null;
    var timing = false;

    // set format for each section
    if (Renderer.ponyoptions.format) {
      cls = Renderer.ponyoptions.format + " " + cls;
    }

    // Set timing option
    if (Renderer.ponyoptions.timing) {
      timing = 'data-timing="'+Renderer.ponyoptions.timing+'"';
    }

    if (self.sections == 0) {

      // heading
      if (self.heading.length > 0) {
        out = self.heading.join('') + "\n";
      }

      out = out+"\n
\n
\n";
    } else {

      if (self.sections == self.hrc.length) {
        out = "\n
\n
\n";
      } else {
        out = "
\n
\n\n
\n
\n";
      }
    }

    self.sections++;

    return out||"";
  }

  Renderer.heading = function (text, level) {
    var out = false;

    // var str = text.split('[');
    // var style = null;
    //
    // switch(str[0]) {
    //   case 'absolute':
    //     str.shift();
    //     style = str.join('');
    //     break;
    //
    //   default:
    // }

    if (self.sections == 0 && self.heading.length == 0 && self.isPonyFile) {
      self.heading.push('\n');
      self.heading.push('<h' + level + '>' + text + '</h' + level + '>\n');
    } else if (self.sections == 0 && self.isPonyFile && (self.heading.length > 1)) {
      self.heading.push('<h' + level + '>' + text + '</h' + level + '>\n');
    } else {
      // default
      out = '<h' + level + ' class="fit">' + text + '</h' + level + '>\n';
    }

    return (out) ? out : "";
  };

  Renderer.paragraph = function(text) {
    var out = null;
    var arr = [];

    text = text.trim();

    // ignore empty text
    if (text.length == 0) {
      return "";
    }

    if (self.sections > 0 || !self.isPonyFile) {

      // add line breaks
      if (text.indexOf("\n") > -1) {
        arr = text.split("\n");
      } else {
        arr.push(text);
      }

      for (x in arr) {
        var img = text.indexOf(" -1) ? "" : "
";
        post = (img > -1) ? "\n" : "
\n";
      }

      out = pre+arr.join('\n\n')+post;

    } else {

      // Add to heading and ignore all other text
      if (self.sections == 0 && self.heading.length > 0) {
        self.heading.push("
"+text+"
\n");
      }

    }
    return out||"";
  }

  // images are rendered as paragraph / text
  Renderer.image = function(href, title, text) {

    var out = null;

		var attr = parseClass(text);

    var out = '' : '>';
    return out;

  }

  Renderer.link = function(href, title, text) {
    if (this.options.sanitize) {
      try {
        var prot = decodeURIComponent(unescape(href))
          .replace(/[^\w:]/g, '')
          .toLowerCase();
      } catch (e) {
        return '';
      }
      if (prot.indexOf('javascript:') === 0) {
        return '';
      }
    }
    var out = '' + text + '';
    return out;
  };

  Renderer.code = function(text,lang) {
		var attr = {};

		// parse style attr if available
		if (lang.indexOf(']') >= 0) {
			lang = lang.split(']');
			attr = lang[0].replace('[','');
			lang = lang[1];
			attr = parseClass(attr);
		}

		switch(lang) {
			case 'xml':
				text = HTMLEncode(text);
				break;
			case 'markup':
				text = HTMLEncode(text);
				break;
		}

		var out = '
";

    return out+''+text+'
';
  }

  Renderer.table = function(header, body) {

    return '
\n'
      + '
\n'
      + header
      + '
\n'
      + '
\n'
      + body
      + '
\n'
      + '
\n';
  }

	Renderer.blockquote = function(quote) {
		var attr = {};

		// parse bracket style attr if available
		if (quote.indexOf('
[') >= 0) {
			var re = /^
\[(.*\S+)?\]/gm;
			var m = re.exec(quote);
			attr = parseClass(m[1]);

			// remove bracket styling
			quote = quote.replace(m[0],'
');

		}

		var out = '
";

	  return out+'\n' + quote + 'TEST
\n';
	};

}

Pony.prototype.setHeader = function(str){
  this.header = str;
}

Pony.prototype.setFooter = function(str){
  this.footer = str;
}

Pony.prototype.setDebug = function(bool) {
  this.debug = bool;
}

Pony.prototype.log = function(args) {
  if (this.debug) {
    console.log(args);
  }
}

Pony.prototype.render = function(dat,opts) {

  var self = this;

  var header, footer, LEXED, htmlClass;

  // Set options (if avail)
  var _globalopts = opts||{};

  // Reset
  this.heading = [];
  this.sections = 0;
  this.hideSlideNumbers = "";
  this.hrc = [];

  var tokens = Kramed.lexer(dat);
	console.log(tokens);

  // Check if first token is configuration
  if (tokens[0].type == "paragraph") {
    if (tokens[0].text.indexOf("\n") > -1) {

      self.isPonyFile = true;

      // rebase tokens
      var rebase = tokens.shift();

      var arr = rebase.text.split("\n");

      // Parse options
      for(var x=0;x<arr.length;x++) {

        opts = arr[x].split(":");

        var _key = opts[0].trim().toLowerCase();
        var _prop = opts[1].trim().toLowerCase();

        self.log(_key + " = " + _prop);

        switch(_key) {

          case 'theme':
            Renderer.ponyoptions.theme = _prop;
            break;

          case 'footer':
            Renderer.ponyoptions.footer = _prop;
            break;

          case 'slidenumbers':
            Renderer.ponyoptions.slidenumbers = (_prop == "false") ? false : true;
            break;

          case 'timing':
            Renderer.ponyoptions.timing = _prop;
            break;

          case 'error':
            self.error = opts[1].trim();
            break;

          case 'tableclass':
            Renderer.ponyoptions.tableclass = _prop||"";
            break;

          case 'format':
            Renderer.ponyoptions.format = _prop||"";
            break;

          case 'progress':
            Renderer.ponyoptions.progress = (_prop == "false") ? false : true;
            break;

          case 'build-lists':
            Renderer.ponyoptions.buildLists = (_prop == "false") ? false : true;
            break;

          case 'transition':
            Renderer.ponyoptions.transition = _prop;
            break;

          case 'shadows':
            Renderer.ponyoptions.shadows = (_prop == "false") ? false : true;
            break;

          default:
            //out = "
"+text+"
";
        }

      }

    }
  }

  self.log(tokens);

  // count HR
  for(var x in tokens) {
    if (tokens[x].type == "hr") {
      self.hrc.push(x);
    }
  }

  if (tokens[self.hrc[self.hrc.length-1]]) {
    tokens[self.hrc[self.hrc.length-1]].last = true;
  }

  // Check if ponyfile
  if (!self.isPonyFile) {
    for(var x=0;x<tokens.length;x++) {
      if (tokens[x].type == "paragraph") {
        if ((tokens[x].text.indexOf("\n---\n") > -1)) {
          self.isPonyFile = true;
        }
      }
    }
  }

  LEXED = Kramed.parser(tokens);

  self.log(LEXED);
  self.log('___________');

  if (LEXED.length == 0) {
    LEXED = self.error;
  }

  // Merge file config with global configs giving preference to file config
  Renderer.ponyoptions = _.assign(_globalopts, Renderer.ponyoptions);

  if (!Renderer.ponyoptions.slidenumbers) {
    self.hideSlideNumbers = '';
  }

  // Apply transition
  if (Renderer.ponyoptions.transition) {
    htmlClass = Renderer.ponyoptions.transition;

    // update defined header
    if (self.header) {
      self.header = self.header.replace(/().*?<head/mgi, '$1 class="'+htmlClass+'"');
    }
  }

  header = self.header||'<!DOCTYPE HTML>' +
  '\n' +
  '\n' +
  	'<title>Pony Show IDE</title>\n' +
  	'<meta charset="utf-8">\n' +
  	'<meta name="viewport" content="width=792, user-scalable=no">\n' +
  	'<meta http-equiv="x-ua-compatible" content="ie=edge">\n' +
  	'<link rel="stylesheet" href="css/main.css">\n' +
  	'<link rel="stylesheet" href="themes/'+Renderer.ponyoptions.theme+'/styles/screen.css">\n' +
  	self.hideSlideNumbers +
  '\n' +
  '\n\n';

  footer = self.footer||'\n' +
  '\n' +
  '';

  if (Renderer.ponyoptions.progress) {
    footer = '\n

\n' + footer;
  }

  // Check if last section.
  if (self.sections == self.hrc.length) {
    LEXED = LEXED+"\n
\n
\n";
  }

  return header+LEXED+footer;

};

if (typeof module !== 'undefined' && typeof exports === 'object') {
  module.exports = new Pony;
} else if (typeof define === 'function' && define.amd) {
  define(function() { return Pony; });
} else {
  this.render = new Pony;
}

}).call(function() {
  return this || (typeof window !== 'undefined' ? window : global);
}());<|endoftext|>


<commit_before>(function(){
    'strict mode';

    global.minify = {};

    var DIR,
        LIBDIR,

        path,
        Util;

    /* Constants */
    exports.LIBDIR      = LIBDIR    = __dirname + '/',
    exports.DIR         = DIR       = LIBDIR + '../',

    /* Functions */
    exports.require                 = mrequire,
    exports.librequire              = librequire,
    exports.rootrequire             = rootrequire,

    /* Native Modules*/
    exports.crypto                  = require('crypto'),
    exports.fs                      = require('fs'),
    exports.path    = path          = require('path'),
    /* compitability with old versions of node */
    exports.fs.exists               = exports.fs.exists || exports.path.exists;

    exports.optimize                = optimize,

    /* Needed Modules */
    exports.util    = Util          = require('util.io');
    exports.mainpackage             = rootrequire('package');

    Util.getExtension               = path.extname;

    global.minify.main = exports;

    var html        = librequire('html'),
        js          = librequire('js'),
        css         = librequire('css');

    function optimize(pParams){
        var lRet = Util.checkObjTrue(pParams, ['ext', 'data']),
            p       = pParams;

        if(lRet)
            switch(p.ext){
                case '.js':
                    p.data = js._uglifyJS(p.data);
                    break;

                case '.html':
                    p.data = html.htmlMinify(p.data);
                    break;

                case '.css':
                    p.data = css._cleanCSS(p.data);
                    break;
            }

        return p.data;
    }

    /**
     * function do safe require of needed module
     * @param {Strin} pSrc
     */
    function mrequire(pSrc){
        var lModule,
        lError = Util.tryCatch(function(){
            lModule = require(pSrc);
        });

        if(lError)
            Util.log(lError);

        return lModule;
    }

    function rootrequire(pSrc){ return mrequire(DIR + pSrc); }

    function librequire(pSrc){ return mrequire(LIBDIR + pSrc); }

})();
<commit_msg>chore(main) add " "<commit_after>(function() {
    'strict mode';

    global.minify = {};

    var DIR,
        LIBDIR,

        path,
        Util;

    /* Constants */
    exports.LIBDIR      = LIBDIR    = __dirname + '/',
    exports.DIR         = DIR       = LIBDIR + '../',

    /* Functions */
    exports.require                 = mrequire,
    exports.librequire              = librequire,
    exports.rootrequire             = rootrequire,

    /* Native Modules*/
    exports.crypto                  = require('crypto'),
    exports.fs                      = require('fs'),
    exports.path    = path          = require('path'),
    /* compitability with old versions of node */
    exports.fs.exists               = exports.fs.exists || exports.path.exists;

    exports.optimize                = optimize,

    /* Needed Modules */
    exports.util    = Util          = require('util.io');
    exports.mainpackage             = rootrequire('package');

    Util.getExtension               = path.extname;

    global.minify.main = exports;

    var html        = librequire('html'),
        js          = librequire('js'),
        css         = librequire('css');

    function optimize(pParams) {
        var lRet    = Util.checkObjTrue(pParams, ['ext', 'data']),
            p       = pParams;

        if(lRet)
            switch(p.ext) {
                case '.js':
                    p.data = js._uglifyJS(p.data);
                    break;

                case '.html':
                    p.data = html.htmlMinify(p.data);
                    break;

                case '.css':
                    p.data = css._cleanCSS(p.data);
                    break;
            }

        return p.data;
    }

    /**
     * function do safe require of needed module
     * @param {Strin} pSrc
     */
    function mrequire(pSrc) {
        var lModule,

        lError = Util.tryCatch(function() {
            lModule = require(pSrc);
        });

        if(lError)
            Util.log(lError);

        return lModule;
    }

    function rootrequire(pSrc) { return mrequire(DIR + pSrc); }

    function librequire(pSrc) { return mrequire(LIBDIR + pSrc); }

})();
<|endoftext|>


<commit_before>import _, { messages } from 'intl'
import ActionButton from 'action-button'
import ActionRowButton from 'action-row-button'
import Component from 'base-component'
import Icon from 'icon'
import map from 'lodash/map'
import React from 'react'
import StateButton from 'state-button'
import Tooltip from 'tooltip'
import { addSubscriptions } from 'utils'
import { alert } from 'modal'
import { Container } from 'grid'
import { Password as EditablePassword, Text } from 'editable'
import { Password, Toggle } from 'form'
import { injectIntl } from 'react-intl'
import {
  addServer,
  editServer,
  connectServer,
  disconnectServer,
  removeServer,
  subscribeServers
} from 'xo'

@addSubscriptions({
  servers: subscribeServers
})

@injectIntl
export default class Servers extends Component {
  _addServer = async () => {
    const { label, host, password, username } = this.state

    await addServer(host, username, password, label)

    this.setState({ label: '', host: '', password: '', username: '' })
  }

  _showError = error => alert(
    error.code === 'SESSION_AUTHENTICATION_FAILED' ? _('serverAuthFailed') : error.code || _('serverUnknownError'),
    error.message
  )

  render () {
    const {
      props: {
        intl: { formatMessage },
        servers
      },
      state
    } = this

    return <Container>

            {_('serverLabel')}
            {_('serverHost')}
            {_('serverUsername')}
            {_('serverPassword')}
            {_('serverStatus')}
            {_('serverReadOnly')}
            {_('serverAction')}

          {map(servers, server => (

                <Text
                  value={server.label || ''}
                  onChange={label => editServer(server, { label })}
                  placeholder={formatMessage(messages.serverPlaceHolderLabel)}
                />

                <Text
                  value={server.host}
                  onChange={host => editServer(server, { host })}
                  placeholder={formatMessage(messages.serverPlaceHolderAddress)}
                />

                <Text
                  value={server.username}
                  onChange={username => editServer(server, { username })}
                  placeholder={formatMessage(messages.serverPlaceHolderUser)}
                />

                <EditablePassword
                  value=''
                  onChange={password => editServer(server, { password })}
                  placeholder={formatMessage(messages.serverPlaceHolderPassword)}
                />

                <StateButton
                  disabledLabel={_('serverDisconnected')}
                  disabledHandler={connectServer}
                  disabledTooltip={_('serverConnect')}

                  enabledLabel={_('serverConnected')}
                  enabledHandler={disconnectServer}
                  enabledTooltip={_('serverDisconnect')}

                  disabled={server.status === 'connecting'}
                  handlerParam={server}
                  state={server.status === 'connected'}
                />
                {' '}
                {server.error &&
                  <Tooltip content={_('serverConnectionFailed')}>
                     this._showError(server.error)}
                    >
                      <Icon
                        icon='alarm'
                        size='lg'
                      />

                  </Tooltip>
                }

              <Toggle value={!!server.readOnly} onChange={readOnly => editServer(server, { readOnly })} />

                <ActionRowButton
                  btnStyle='danger'
                  handler={removeServer}
                  handlerParam={server}
                  icon='delete'
                  style={{
                    marginRight: '0.5em'
                  }}
                />

          ))}

      <form
        className='form-inline'
        id='form-add-server'
      >

          <input
            className='form-control'
            onChange={this.linkState('label')}
            placeholder={formatMessage(messages.serverPlaceHolderLabel)}
            type='text'
            value={state.label}
          />

        {' '}

          <input
            className='form-control'
            onChange={this.linkState('host')}
            placeholder={formatMessage(messages.serverPlaceHolderAddress)}
            required
            type='text'
            value={state.host}
          />

        {' '}

          <input
            className='form-control'
            onChange={this.linkState('username')}
            placeholder={formatMessage(messages.serverPlaceHolderUser)}
            required
            type='text'
            value={state.username}
          />

        {' '}

          <Password
            disabled={!this.state.username}
            onChange={this.linkState('password')}
            placeholder={formatMessage(messages.serverPlaceHolderPassword)}
            required
            value={state.password}
          />

        {' '}
        <ActionButton
          btnStyle='primary'
          form='form-add-server'
          handler={this._addServer}
          icon='save'
        >
          {_('serverConnect')}
        </ActionButton>
      </form>
    </Container>
  }
}
<commit_msg>feat(servers): add pending status<commit_after>import _, { messages } from 'intl'
import ActionButton from 'action-button'
import ActionRowButton from 'action-row-button'
import Component from 'base-component'
import Icon from 'icon'
import map from 'lodash/map'
import React from 'react'
import StateButton from 'state-button'
import Tooltip from 'tooltip'
import { addSubscriptions } from 'utils'
import { alert } from 'modal'
import { Container } from 'grid'
import { Password as EditablePassword, Text } from 'editable'
import { Password, Toggle } from 'form'
import { injectIntl } from 'react-intl'
import {
  addServer,
  editServer,
  connectServer,
  disconnectServer,
  removeServer,
  subscribeServers
} from 'xo'

@addSubscriptions({
  servers: subscribeServers
})

@injectIntl
export default class Servers extends Component {
  _addServer = async () => {
    const { label, host, password, username } = this.state

    await addServer(host, username, password, label)

    this.setState({ label: '', host: '', password: '', username: '' })
  }

  _showError = error => alert(
    error.code === 'SESSION_AUTHENTICATION_FAILED' ? _('serverAuthFailed') : error.code || _('serverUnknownError'),
    error.message
  )

  render () {
    const {
      props: {
        intl: { formatMessage },
        servers
      },
      state
    } = this

    return <Container>

            {_('serverLabel')}
            {_('serverHost')}
            {_('serverUsername')}
            {_('serverPassword')}
            {_('serverStatus')}
            {_('serverReadOnly')}
            {_('serverAction')}

          {map(servers, server => (

                <Text
                  value={server.label || ''}
                  onChange={label => editServer(server, { label })}
                  placeholder={formatMessage(messages.serverPlaceHolderLabel)}
                />

                <Text
                  value={server.host}
                  onChange={host => editServer(server, { host })}
                  placeholder={formatMessage(messages.serverPlaceHolderAddress)}
                />

                <Text
                  value={server.username}
                  onChange={username => editServer(server, { username })}
                  placeholder={formatMessage(messages.serverPlaceHolderUser)}
                />

                <EditablePassword
                  value=''
                  onChange={password => editServer(server, { password })}
                  placeholder={formatMessage(messages.serverPlaceHolderPassword)}
                />

                <StateButton
                  disabledLabel={_('serverDisconnected')}
                  disabledHandler={connectServer}
                  disabledTooltip={_('serverConnect')}

                  enabledLabel={_('serverConnected')}
                  enabledHandler={disconnectServer}
                  enabledTooltip={_('serverDisconnect')}

                  handlerParam={server}
                  pending={server.status === 'connecting'}
                  state={server.status === 'connected'}
                />
                {' '}
                {server.error &&
                  <Tooltip content={_('serverConnectionFailed')}>
                     this._showError(server.error)}
                    >
                      <Icon
                        icon='alarm'
                        size='lg'
                      />

                  </Tooltip>
                }

              <Toggle value={!!server.readOnly} onChange={readOnly => editServer(server, { readOnly })} />

                <ActionRowButton
                  btnStyle='danger'
                  handler={removeServer}
                  handlerParam={server}
                  icon='delete'
                  style={{
                    marginRight: '0.5em'
                  }}
                />

          ))}

      <form
        className='form-inline'
        id='form-add-server'
      >

          <input
            className='form-control'
            onChange={this.linkState('label')}
            placeholder={formatMessage(messages.serverPlaceHolderLabel)}
            type='text'
            value={state.label}
          />

        {' '}

          <input
            className='form-control'
            onChange={this.linkState('host')}
            placeholder={formatMessage(messages.serverPlaceHolderAddress)}
            required
            type='text'
            value={state.host}
          />

        {' '}

          <input
            className='form-control'
            onChange={this.linkState('username')}
            placeholder={formatMessage(messages.serverPlaceHolderUser)}
            required
            type='text'
            value={state.username}
          />

        {' '}

          <Password
            disabled={!this.state.username}
            onChange={this.linkState('password')}
            placeholder={formatMessage(messages.serverPlaceHolderPassword)}
            required
            value={state.password}
          />

        {' '}
        <ActionButton
          btnStyle='primary'
          form='form-add-server'
          handler={this._addServer}
          icon='save'
        >
          {_('serverConnect')}
        </ActionButton>
      </form>
    </Container>
  }
}
<|endoftext|>