<commit_before>// Package of defining Vegalite Specification's json schema
"use strict";

var schema = module.exports = {},
  util = require('../util');

schema.util = require('./schemautil');

schema.marktype = {
  type: 'string',
  enum: ['point', 'tick', 'bar', 'line', 'area', 'circle', 'square', 'text']
};

schema.aggr = {
  type: 'string',
  enum: ['avg', 'sum', 'min', 'max', 'count'],
  supportedEnums: {
    Q: ['avg', 'sum', 'min', 'max', 'count'],
    O: [],
    T: ['avg', 'min', 'max'],
    '': ['count']
  },
  supportedTypes: {'Q': true, 'O': true, 'T': true, '': true}
};
schema.band = {
  type: 'object',
  properties: {
    size: {
      type: 'integer',
      minimum: 0
    },
    padding: {
      type: 'integer',
      minimum: 0,
      default: 1
    }
  }
};

schema.getSupportedRole = function(encType) {
  return schema.schema.properties.enc.properties[encType].supportedRole;
};

schema.timefns = ['year', 'month', 'day', 'date', 'hours', 'minutes', 'seconds'];

schema.defaultTimeFn = 'month';

schema.fn = {
  type: 'string',
  enum: schema.timefns,
  supportedTypes: {'T': true}
};

//TODO(kanitw): add other type of function here

schema.scale_type = {
  type: 'string',
  enum: ['linear', 'log', 'pow', 'sqrt', 'quantile'],
  default: 'linear',
  supportedTypes: {'Q': true}
};

schema.field = {
  type: 'object',
  properties: {
    name: {
      type: 'string'
    }
  }
};

var clone = util.duplicate;
var merge = schema.util.merge;

schema.MAXBINS_DEFAULT = 15;

var bin = {
  type: ['boolean', 'object'],
  default: false,
  properties: {
    maxbins: {
      type: 'integer',
      default: schema.MAXBINS_DEFAULT,
      minimum: 2
    }
  },
  supportedTypes: {'Q': true} // TODO: add 'O' after finishing #81
};

var typicalField = merge(clone(schema.field), {
  type: 'object',
  properties: {
    type: {
      type: 'string',
      enum: ['O', 'Q', 'T']
    },
    aggr: schema.aggr,
    fn: schema.fn,
    bin: bin,
    scale: {
      type: 'object',
      properties: {
        type: schema.scale_type,
        reverse: {
          type: 'boolean',
          default: false,
          supportedTypes: {'Q': true, 'T': true}
        },
        zero: {
          type: 'boolean',
          description: 'Include zero',
          default: true,
          supportedTypes: {'Q': true, 'T': true}
        },
        nice: {
          type: 'string',
          enum: ['second', 'minute', 'hour', 'day', 'week', 'month', 'year'],
          supportedTypes: {'T': true}
        }
      }
    }
  }
});

var onlyOrdinalField = merge(clone(schema.field), {
  type: 'object',
  supportedRole: {
    dimension: true
  },
  properties: {
    type: {
      type: 'string',
      enum: ['O','Q', 'T'] // ordinal-only field supports Q when bin is applied and T when fn is applied.
    },
    fn: schema.fn,
    bin: bin,
    aggr: {
      type: 'string',
      enum: ['count'],
      supportedTypes: {'O': true}
    }
  }
});

var axisMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true},
  properties: {
    axis: {
      type: 'object',
      properties: {
        grid: {
          type: 'boolean',
          default: true,
          description: 'A flag indicate if gridlines should be created in addition to ticks.'
        },
        title: {
          type: 'boolean',
          default: true,
          description: 'A title for the axis.'
        },
        titleOffset: {
          type: 'integer',
          default: undefined,  // auto
          description: 'A title offset value for the axis.'
        },
        format: {
          type: 'string',
          default: undefined,  // auto
          description: 'The formatting pattern for axis labels.'
        },
        maxLabelLength: {
          type: 'integer',
          default: 25,
          minimum: 0,
          description: 'Truncate labels that are too long.'
        }
      }
    }
  }
};

var sortMixin = {
  type: 'object',
  properties: {
    sort: {
      type: 'array',
      default: [],
      items: {
        type: 'object',
        supportedTypes: {'O': true},
        required: ['name', 'aggr'],
        name: {
          type: 'string'
        },
        aggr: {
          type: 'string',
          enum: ['avg', 'sum', 'min', 'max', 'count']
        },
        reverse: {
          type: 'boolean',
          default: false
        }
      }
    }
  }
};

var bandMixin = {
  type: 'object',
  properties: {
    band: schema.band
  }
};

var legendMixin = {
  type: 'object',
  properties: {
    legend: {
      type: 'boolean',
      default: true
    }
  }
};

var textMixin = {
  type: 'object',
  supportedMarktypes: {'text': true},
  properties: {
    text: {
      type: 'object',
      properties: {
        align: {
          type: 'string',
          default: 'left'
        },
        baseline: {
          type: 'string',
          default: 'middle'
        },
        margin: {
          type: 'integer',
          default: 4,
          minimum: 0
        }
      }
    },
    font: {
      type: 'object',
      properties: {
        weight: {
          type: 'string',
          enum: ['normal', 'bold'],
          default: 'normal'
        },
        size: {
          type: 'integer',
          default: 10,
          minimum: 0
        },
        family: {
          type: 'string',
          default: 'Helvetica Neue'
        },
        style: {
          type: 'string',
          default: 'normal',
          enum: ['normal', 'italic']
        }
      }
    }
  }
};

var sizeMixin = {
  type: 'object',
  supportedMarktypes: {point: true, bar: true, circle: true, square: true, text: true},
  properties: {
    value: {
      type: 'integer',
      default: 30,
      minimum: 0
    }
  }
};

var colorMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true, 'text': true},
  properties: {
    value: {
      type: 'string',
      role: 'color',
      default: 'steelblue'
    },
    scale: {
      type: 'object',
      properties: {
        range: {
          type: ['string', 'array']
        }
      }
    }
  }
};

var alphaMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true, 'text': true},
  properties: {
    value: {
      type: 'number',
      default: undefined,  // auto
      minimum: 0,
      maximum: 1
    }
  }
};

var shapeMixin = {
  type: 'object',
  supportedMarktypes: {point: true, circle: true, square: true},
  properties: {
    value: {
      type: 'string',
      enum: ['circle', 'square', 'cross', 'diamond', 'triangle-up', 'triangle-down'],
      default: 'circle'
    }
  }
};

var detailMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, line: true, circle: true, square: true}
};

var rowMixin = {
  properties: {
    height: {
      type: 'number',
      minimum: 0,
      default: 150
    },
    grid: {
      type: 'boolean',
      default: true,
      description: 'A flag indicate if gridlines should be created in addition to ticks.'
    },
  }
};

var colMixin = {
  properties: {
    width: {
      type: 'number',
      minimum: 0,
      default: 150
    },
    axis: {
      properties: {
        maxLabelLength: {
          type: 'integer',
          default: 12,
          minimum: 0,
          description: 'Truncate labels that are too long.'
        }
      }
    }
  }
};

var facetMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true, text: true},
  properties: {
    padding: {
      type: 'number',
      minimum: 0,
      maximum: 1,
      default: 0.1
    }
  }
};

var requiredNameType = {
  required: ['name', 'type']
};

var multiRoleField = merge(clone(typicalField), {
  supportedRole: {
    measure: true,
    dimension: true
  }
});

var quantitativeField = merge(clone(typicalField), {
  supportedRole: {
    measure: true,
    dimension: 'ordinal-only' // using alpha / size to encoding category lead to order interpretation
  }
});

var onlyQuantitativeField = merge(clone(typicalField), {
  supportedRole: {
    measure: true
  }
});

var x = merge(clone(multiRoleField), axisMixin, bandMixin, requiredNameType, sortMixin);
var y = clone(x);

var facet = merge(clone(onlyOrdinalField), requiredNameType, facetMixin, sortMixin);
var row = merge(clone(facet), axisMixin, rowMixin);
var col = merge(clone(facet), axisMixin, colMixin);

var size = merge(clone(quantitativeField), legendMixin, sizeMixin, sortMixin);
var color = merge(clone(multiRoleField), legendMixin, colorMixin, sortMixin);
var alpha = merge(clone(quantitativeField), alphaMixin, sortMixin);
var shape = merge(clone(onlyOrdinalField), legendMixin, shapeMixin, sortMixin);
var detail = merge(clone(onlyOrdinalField), detailMixin, sortMixin);

// we only put aggregated measure in pivot table
var text = merge(clone(onlyQuantitativeField), textMixin, sortMixin);

// TODO add label

var filter = {
  type: 'array',
  items: {
    type: 'object',
    properties: {
      operands: {
        type: 'array',
        items: {
          type: ['string', 'boolean', 'integer', 'number']
        }
      },
      operator: {
        type: 'string',
        enum: ['>', '>=', '=', '!=', '<', '<=', 'notNull']
      }
    }
  }
};

var data = {
  type: 'object',
  properties: {
    // data source
    formatType: {
      type: 'string',
      enum: ['json', 'csv'],
      default: 'json'
    },
    url: {
      type: 'string',
      default: undefined
    },
    values: {
      type: 'array',
      description: 'Pass array of objects instead of a url to a file.',
      items: {
        type: 'object',
        additionalProperties: true
      }
    }
  }
};

var config = {
  type: 'object',
  properties: {
    // template
    width: {
      type: 'integer',
      default: undefined
    },
    height: {
      type: 'integer',
      default: undefined
    },
    viewport: {
      type: 'array',
      items: {
        type: 'integer'
      },
      default: undefined
    },
    gridColor: {
      type: 'string',
      role: 'color',
      default: '#000'
    },
    gridOpacity: {
      type: 'number',
      minimum: 0,
      maximum: 1,
      default: 0.15
    },

    // filter null
    filterNull: {
      type: 'object',
      properties: {
        O: {type:'boolean', default: false},
        Q: {type:'boolean', default: true},
        T: {type:'boolean', default: true}
      }
    },
    toggleSort: {
      type: 'string',
      default: 'O'
    },

    // single plot
    singleHeight: {
      // will be overwritten by bandWidth * (cardinality + padding)
      type: 'integer',
      default: 200,
      minimum: 0
    },
    singleWidth: {
      // will be overwritten by bandWidth * (cardinality + padding)
      type: 'integer',
      default: 200,
      minimum: 0
    },
    // band size
    largeBandSize: {
      type: 'integer',
      default: 21,
      minimum: 0
    },
    smallBandSize: {
      //small multiples or single plot with high cardinality
      type: 'integer',
      default: 12,
      minimum: 0
    },
    largeBandMaxCardinality: {
      type: 'integer',
      default: 10
    },
    // small multiples
    cellPadding: {
      type: 'number',
      default: 0.1
    },
    cellGridColor: {
      type: 'string',
      role: 'color',
      default: '#aaaaaa'
    },
    cellBackgroundColor: {
      type: 'string',
      role: 'color',
      default: 'transparent'
    },
    textCellWidth: {
      type: 'integer',
      default: 90,
      minimum: 0
    },

    // marks
    strokeWidth: {
      type: 'integer',
      default: 2,
      minimum: 0
    },

    // scales
    timeScaleLabelLength: {
      type: 'integer',
      default: 3,
      minimum: 0
    },
    // other
    characterWidth: {
      type: 'integer',
      default: 6
    }
  }
};

/** @type Object Schema of a vegalite specification */
schema.schema = {
  $schema: 'http://json-schema.org/draft-04/schema#',
  description: 'Schema for vegalite specification',
  type: 'object',
  required: ['marktype', 'enc', 'data'],
  properties: {
    data: data,
    marktype: schema.marktype,
    enc: {
      type: 'object',
      properties: {
        x: x,
        y: y,
        row: row,
        col: col,
        size: size,
        color: color,
        alpha: alpha,
        shape: shape,
        text: text,
        detail: detail
      }
    },
    filter: filter,
    config: config
  }
};

schema.encTypes = util.keys(schema.schema.properties.enc.properties);

/** Instantiate a verbose vl spec from the schema */
schema.instantiate = function() {
  return schema.util.instantiate(schema.schema);
};
<commit_msg>adjust parameter<commit_after>// Package of defining Vegalite Specification's json schema
"use strict";

var schema = module.exports = {},
  util = require('../util');

schema.util = require('./schemautil');

schema.marktype = {
  type: 'string',
  enum: ['point', 'tick', 'bar', 'line', 'area', 'circle', 'square', 'text']
};

schema.aggr = {
  type: 'string',
  enum: ['avg', 'sum', 'min', 'max', 'count'],
  supportedEnums: {
    Q: ['avg', 'sum', 'min', 'max', 'count'],
    O: [],
    T: ['avg', 'min', 'max'],
    '': ['count']
  },
  supportedTypes: {'Q': true, 'O': true, 'T': true, '': true}
};
schema.band = {
  type: 'object',
  properties: {
    size: {
      type: 'integer',
      minimum: 0
    },
    padding: {
      type: 'integer',
      minimum: 0,
      default: 1
    }
  }
};

schema.getSupportedRole = function(encType) {
  return schema.schema.properties.enc.properties[encType].supportedRole;
};

schema.timefns = ['year', 'month', 'day', 'date', 'hours', 'minutes', 'seconds'];

schema.defaultTimeFn = 'month';

schema.fn = {
  type: 'string',
  enum: schema.timefns,
  supportedTypes: {'T': true}
};

//TODO(kanitw): add other type of function here

schema.scale_type = {
  type: 'string',
  enum: ['linear', 'log', 'pow', 'sqrt', 'quantile'],
  default: 'linear',
  supportedTypes: {'Q': true}
};

schema.field = {
  type: 'object',
  properties: {
    name: {
      type: 'string'
    }
  }
};

var clone = util.duplicate;
var merge = schema.util.merge;

schema.MAXBINS_DEFAULT = 15;

var bin = {
  type: ['boolean', 'object'],
  default: false,
  properties: {
    maxbins: {
      type: 'integer',
      default: schema.MAXBINS_DEFAULT,
      minimum: 2
    }
  },
  supportedTypes: {'Q': true} // TODO: add 'O' after finishing #81
};

var typicalField = merge(clone(schema.field), {
  type: 'object',
  properties: {
    type: {
      type: 'string',
      enum: ['O', 'Q', 'T']
    },
    aggr: schema.aggr,
    fn: schema.fn,
    bin: bin,
    scale: {
      type: 'object',
      properties: {
        type: schema.scale_type,
        reverse: {
          type: 'boolean',
          default: false,
          supportedTypes: {'Q': true, 'T': true}
        },
        zero: {
          type: 'boolean',
          description: 'Include zero',
          default: true,
          supportedTypes: {'Q': true, 'T': true}
        },
        nice: {
          type: 'string',
          enum: ['second', 'minute', 'hour', 'day', 'week', 'month', 'year'],
          supportedTypes: {'T': true}
        }
      }
    }
  }
});

var onlyOrdinalField = merge(clone(schema.field), {
  type: 'object',
  supportedRole: {
    dimension: true
  },
  properties: {
    type: {
      type: 'string',
      enum: ['O','Q', 'T'] // ordinal-only field supports Q when bin is applied and T when fn is applied.
    },
    fn: schema.fn,
    bin: bin,
    aggr: {
      type: 'string',
      enum: ['count'],
      supportedTypes: {'O': true}
    }
  }
});

var axisMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true},
  properties: {
    axis: {
      type: 'object',
      properties: {
        grid: {
          type: 'boolean',
          default: true,
          description: 'A flag indicate if gridlines should be created in addition to ticks.'
        },
        title: {
          type: 'boolean',
          default: true,
          description: 'A title for the axis.'
        },
        titleOffset: {
          type: 'integer',
          default: undefined,  // auto
          description: 'A title offset value for the axis.'
        },
        format: {
          type: 'string',
          default: undefined,  // auto
          description: 'The formatting pattern for axis labels.'
        },
        maxLabelLength: {
          type: 'integer',
          default: 25,
          minimum: 0,
          description: 'Truncate labels that are too long.'
        }
      }
    }
  }
};

var sortMixin = {
  type: 'object',
  properties: {
    sort: {
      type: 'array',
      default: [],
      items: {
        type: 'object',
        supportedTypes: {'O': true},
        required: ['name', 'aggr'],
        name: {
          type: 'string'
        },
        aggr: {
          type: 'string',
          enum: ['avg', 'sum', 'min', 'max', 'count']
        },
        reverse: {
          type: 'boolean',
          default: false
        }
      }
    }
  }
};

var bandMixin = {
  type: 'object',
  properties: {
    band: schema.band
  }
};

var legendMixin = {
  type: 'object',
  properties: {
    legend: {
      type: 'boolean',
      default: true
    }
  }
};

var textMixin = {
  type: 'object',
  supportedMarktypes: {'text': true},
  properties: {
    text: {
      type: 'object',
      properties: {
        align: {
          type: 'string',
          default: 'left'
        },
        baseline: {
          type: 'string',
          default: 'middle'
        },
        margin: {
          type: 'integer',
          default: 4,
          minimum: 0
        }
      }
    },
    font: {
      type: 'object',
      properties: {
        weight: {
          type: 'string',
          enum: ['normal', 'bold'],
          default: 'normal'
        },
        size: {
          type: 'integer',
          default: 10,
          minimum: 0
        },
        family: {
          type: 'string',
          default: 'Helvetica Neue'
        },
        style: {
          type: 'string',
          default: 'normal',
          enum: ['normal', 'italic']
        }
      }
    }
  }
};

var sizeMixin = {
  type: 'object',
  supportedMarktypes: {point: true, bar: true, circle: true, square: true, text: true},
  properties: {
    value: {
      type: 'integer',
      default: 30,
      minimum: 0
    }
  }
};

var colorMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true, 'text': true},
  properties: {
    value: {
      type: 'string',
      role: 'color',
      default: 'steelblue'
    },
    scale: {
      type: 'object',
      properties: {
        range: {
          type: ['string', 'array']
        }
      }
    }
  }
};

var alphaMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true, 'text': true},
  properties: {
    value: {
      type: 'number',
      default: undefined,  // auto
      minimum: 0,
      maximum: 1
    }
  }
};

var shapeMixin = {
  type: 'object',
  supportedMarktypes: {point: true, circle: true, square: true},
  properties: {
    value: {
      type: 'string',
      enum: ['circle', 'square', 'cross', 'diamond', 'triangle-up', 'triangle-down'],
      default: 'circle'
    }
  }
};

var detailMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, line: true, circle: true, square: true}
};

var rowMixin = {
  properties: {
    height: {
      type: 'number',
      minimum: 0,
      default: 150
    },
    grid: {
      type: 'boolean',
      default: true,
      description: 'A flag indicate if gridlines should be created in addition to ticks.'
    },
  }
};

var colMixin = {
  properties: {
    width: {
      type: 'number',
      minimum: 0,
      default: 150
    },
    axis: {
      properties: {
        maxLabelLength: {
          type: 'integer',
          default: 12,
          minimum: 0,
          description: 'Truncate labels that are too long.'
        }
      }
    }
  }
};

var facetMixin = {
  type: 'object',
  supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true, text: true},
  properties: {
    padding: {
      type: 'number',
      minimum: 0,
      maximum: 1,
      default: 0.1
    }
  }
};

var requiredNameType = {
  required: ['name', 'type']
};

var multiRoleField = merge(clone(typicalField), {
  supportedRole: {
    measure: true,
    dimension: true
  }
});

var quantitativeField = merge(clone(typicalField), {
  supportedRole: {
    measure: true,
    dimension: 'ordinal-only' // using alpha / size to encoding category lead to order interpretation
  }
});

var onlyQuantitativeField = merge(clone(typicalField), {
  supportedRole: {
    measure: true
  }
});

var x = merge(clone(multiRoleField), axisMixin, bandMixin, requiredNameType, sortMixin);
var y = clone(x);

var facet = merge(clone(onlyOrdinalField), requiredNameType, facetMixin, sortMixin);
var row = merge(clone(facet), axisMixin, rowMixin);
var col = merge(clone(facet), axisMixin, colMixin);

var size = merge(clone(quantitativeField), legendMixin, sizeMixin, sortMixin);
var color = merge(clone(multiRoleField), legendMixin, colorMixin, sortMixin);
var alpha = merge(clone(quantitativeField), alphaMixin, sortMixin);
var shape = merge(clone(onlyOrdinalField), legendMixin, shapeMixin, sortMixin);
var detail = merge(clone(onlyOrdinalField), detailMixin, sortMixin);

// we only put aggregated measure in pivot table
var text = merge(clone(onlyQuantitativeField), textMixin, sortMixin);

// TODO add label

var filter = {
  type: 'array',
  items: {
    type: 'object',
    properties: {
      operands: {
        type: 'array',
        items: {
          type: ['string', 'boolean', 'integer', 'number']
        }
      },
      operator: {
        type: 'string',
        enum: ['>', '>=', '=', '!=', '<', '<=', 'notNull']
      }
    }
  }
};

var data = {
  type: 'object',
  properties: {
    // data source
    formatType: {
      type: 'string',
      enum: ['json', 'csv'],
      default: 'json'
    },
    url: {
      type: 'string',
      default: undefined
    },
    values: {
      type: 'array',
      description: 'Pass array of objects instead of a url to a file.',
      items: {
        type: 'object',
        additionalProperties: true
      }
    }
  }
};

var config = {
  type: 'object',
  properties: {
    // template
    width: {
      type: 'integer',
      default: undefined
    },
    height: {
      type: 'integer',
      default: undefined
    },
    viewport: {
      type: 'array',
      items: {
        type: 'integer'
      },
      default: undefined
    },
    gridColor: {
      type: 'string',
      role: 'color',
      default: '#000'
    },
    gridOpacity: {
      type: 'number',
      minimum: 0,
      maximum: 1,
      default: 0.08
    },

    // filter null
    filterNull: {
      type: 'object',
      properties: {
        O: {type:'boolean', default: false},
        Q: {type:'boolean', default: true},
        T: {type:'boolean', default: true}
      }
    },
    toggleSort: {
      type: 'string',
      default: 'O'
    },

    // single plot
    singleHeight: {
      // will be overwritten by bandWidth * (cardinality + padding)
      type: 'integer',
      default: 200,
      minimum: 0
    },
    singleWidth: {
      // will be overwritten by bandWidth * (cardinality + padding)
      type: 'integer',
      default: 200,
      minimum: 0
    },
    // band size
    largeBandSize: {
      type: 'integer',
      default: 21,
      minimum: 0
    },
    smallBandSize: {
      //small multiples or single plot with high cardinality
      type: 'integer',
      default: 12,
      minimum: 0
    },
    largeBandMaxCardinality: {
      type: 'integer',
      default: 10
    },
    // small multiples
    cellPadding: {
      type: 'number',
      default: 0.1
    },
    cellGridColor: {
      type: 'string',
      role: 'color',
      default: '#aaaaaa'
    },
    cellBackgroundColor: {
      type: 'string',
      role: 'color',
      default: 'transparent'
    },
    textCellWidth: {
      type: 'integer',
      default: 90,
      minimum: 0
    },

    // marks
    strokeWidth: {
      type: 'integer',
      default: 2,
      minimum: 0
    },

    // scales
    timeScaleLabelLength: {
      type: 'integer',
      default: 3,
      minimum: 0
    },
    // other
    characterWidth: {
      type: 'integer',
      default: 6
    }
  }
};

/** @type Object Schema of a vegalite specification */
schema.schema = {
  $schema: 'http://json-schema.org/draft-04/schema#',
  description: 'Schema for vegalite specification',
  type: 'object',
  required: ['marktype', 'enc', 'data'],
  properties: {
    data: data,
    marktype: schema.marktype,
    enc: {
      type: 'object',
      properties: {
        x: x,
        y: y,
        row: row,
        col: col,
        size: size,
        color: color,
        alpha: alpha,
        shape: shape,
        text: text,
        detail: detail
      }
    },
    filter: filter,
    config: config
  }
};

schema.encTypes = util.keys(schema.schema.properties.enc.properties);

/** Instantiate a verbose vl spec from the schema */
schema.instantiate = function() {
  return schema.util.instantiate(schema.schema);
};
<|endoftext|>


<commit_before>
function handleAgentLoaded() {

    var debugOutput = document.querySelector( "#debug" );
    if ( !debugOutput ) { return; }
    debugOutput.innerHTML = "Agent loaded";

}
handleAgentLoaded();
window.addEventListener( "load", handleAgentLoaded );

window.addEventListener( "message", function( e ) {

    var debugOutput = document.querySelector( "#debug" );
    function log() {

        if ( debugOutput ) { debugOutput.innerHTML = [].join.call( arguments, " " ); }
        else { console.log.apply( console, arguments ); }

    }
    var origin = `${origin}`;
    log( "Agent received", e, e.origin, origin );
    if( e.origin !== origin ){ return; }

    var cid = e.data && e.data.cid;

    function reply( err ) {

        log( "Agent replying to", cid, err );
        window.parent.postMessage( {

            type: "agent-response",
            cid: e.data.cid,
            isSuccess: !err,
            args: err ? err.stack : [].slice.call( arguments, 1 )

        }, e.origin );

    }

    try {

        log( "Agent running", cid );
        var func = new Function( "reply", e.data.js );
        func( reply );

    } catch( e ) {

        reply( e );

    }

} );<commit_msg>Agent is now slightly less verbose<commit_after>
function handleAgentLoaded() {

    var debugOutput = document.querySelector( "#debug" );
    if ( !debugOutput ) { return; }
    debugOutput.innerHTML = "Agent loaded";

}
handleAgentLoaded();
window.addEventListener( "load", handleAgentLoaded );

window.addEventListener( "message", function( e ) {

    var debugOutput = document.querySelector( "#debug" );
    function log() {

        if ( debugOutput ) { debugOutput.innerHTML = [].join.call( arguments, " " ); }
        else { console.log.apply( console, arguments ); }

    }
    var origin = `${origin}`;
    if( e.origin !== origin ){ return; }
    log( "Agent received", e, e.origin, origin );

    var cid = e.data && e.data.cid;

    function reply( err ) {

        log( "Agent replying to", cid, err );
        window.parent.postMessage( {

            type: "agent-response",
            cid: e.data.cid,
            isSuccess: !err,
            args: err ? err.stack : [].slice.call( arguments, 1 )

        }, e.origin );

    }

    try {

        log( "Agent running", cid );
        var func = new Function( "reply", e.data.js );
        func( reply );

    } catch( e ) {

        reply( e );

    }

} );<|endoftext|>


<commit_before>import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react';

import App from './containers/App.jsx';
import Socket from './utils/Websocket.js';
import configureStore from './store/configureStore';
import * as ActionTypes from './constants/ActionTypes.js';
import InitialState from './constants/InitialState.js';

const store = configureStore(InitialState);
const websocket = {
    connection: null,
    uri: 'localhost:1337',
    dispatcher: message => {
        const state = store.getState();
        console.log('[IN INDEX.JS] -> \nWebsocket dispatching an action');
        console.dir(state);
        console.dir(message);
        return store.dispatch(message);
    },
    listeners: () => {
        const { login } = store.getState();
        console.log(`login state: ${login}`);
	switch (login.type) {
	    case ActionTypes.LOGIN_REQUEST:
                console.log("[IN INDEX.JS] -> \nHandling login request");
	        return websocket.connection.write(login.id, login.type, login.credentials);

	    case ActionTypes.FETCH_FILES_REQUEST:
	        return websocket.connection.write(login.id, login.type, login.dirpath || '/');

	    default:
                console.log(`[IN INDEX.JS] -> \nHandling invalid request: ${login}`);
	        return;
        }
    }
}

render(

        <Provider store={store}>
            <App/>
        </Provider>
        <DebugPanel top right bottom>
            <DevTools store={store} monitor={LogMonitor} visibleOnLoad={true} />
        </DebugPanel>

,
    document.getElementById('app')
)

websocket.connection = new Socket(websocket.uri, websocket.dispatcher);
store.subscribe(() => websocket.listeners());

<commit_msg>Updated login state comment<commit_after>import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react';

import App from './containers/App.jsx';
import Socket from './utils/Websocket.js';
import configureStore from './store/configureStore';
import * as ActionTypes from './constants/ActionTypes.js';
import InitialState from './constants/InitialState.js';

const store = configureStore(InitialState);
const websocket = {
    connection: null,
    uri: 'localhost:1337',
    dispatcher: message => {
        const state = store.getState();
        console.log('[IN INDEX.JS] -> \nWebsocket dispatching an action');
        console.dir(state);
        console.dir(message);
        return store.dispatch(message);
    },
    listeners: () => {
        const { login } = store.getState();
        console.log('[IN INDEX.JS] -> login state:');
        console.dir(login);
	switch (login.type) {
	    case ActionTypes.LOGIN_REQUEST:
                console.log("[IN INDEX.JS] -> \nHandling login request");
	        return websocket.connection.write(login.id, login.type, login.credentials);

	    case ActionTypes.FETCH_FILES_REQUEST:
	        return websocket.connection.write(login.id, login.type, login.dirpath || '/');

	    default:
                console.log(`[IN INDEX.JS] -> \nHandling invalid request: ${login}`);
	        return;
        }
    }
}

render(

        <Provider store={store}>
            <App/>
        </Provider>
        <DebugPanel top right bottom>
            <DevTools store={store} monitor={LogMonitor} visibleOnLoad={true} />
        </DebugPanel>

,
    document.getElementById('app')
)

websocket.connection = new Socket(websocket.uri, websocket.dispatcher);
store.subscribe(() => websocket.listeners());

<|endoftext|>


<commit_before>/*
 *  Seni
 *  Copyright (C) 2015  Inderjit Gill <email@indy.io>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import PublicBinding from './PublicBinding';
import Immutable from 'immutable';

const PI = Math.PI;
const TAU = PI * 2;
const PIbyTwo = PI / 2;

const degToRad = TAU / 360;
const radToDeg = 360 / TAU;

function degreesToRadians(angle) {
  return (angle % 360) * degToRad;
}

function radiansToDegrees(angle) {
  return (angle % TAU) * radToDeg;
}

function mc([xa, ya], [xb, yb]) {
  const m = (ya - yb) / (xa - xb);
  const c = ya - (m * xa);
  return [m, c];
}

// the following map* functions work in the range 0..1:

function mapLinear(x) {
  return x;
}

function mapQuickEase(x) {
  const x2 = x * x;
  const x3 = x * x * x;
  return (3 * x2) - (2 * x3);
}

function mapSlowEaseIn(x) {
  const s = Math.sin(x * PIbyTwo);
  return s * s * s * s;
}

function mapSlowEaseInEaseOut(x) {
  return x - (Math.sin(x * TAU) / TAU);
}

const remappingFn = new Immutable.Map({'linear': mapLinear,
                                       'quick': mapQuickEase,
                                       'slow-in': mapSlowEaseIn,
                                       'slow-in-out': mapSlowEaseInEaseOut});

function remapFn(params) {

  const from = params.from || [0, 1];
  const to = params.to || [0, 100];
  const clamping = params.clamping || false;
  const mapping = params.mapping || 'linear';

  const [fromA, fromB] = from;
  const [toA, toB] = to;
  const [fromM, fromC] = mc([fromA, 0], [fromB, 1]);
  const [toM, toC] = mc([0, toA], [1, toB]);

  let normalisedMappingFn = remappingFn.get(mapping);

  if (normalisedMappingFn === undefined) {
    normalisedMappingFn = remappingFn.get('linear');
  }

  return function(parameters) {
    const val = parameters.val || 0;
    const fromInterp = (fromM * val) + fromC;
    const toInterp = normalisedMappingFn(fromInterp);
    const res = (toM * toInterp) + toC;

    if (clamping) {
      return fromInterp < 0 ? toA : (fromInterp > 1) ? toB : res;
    } else {
      return res;
    }
  };
}

function normalize(x, y) {
  const len = Math.sqrt((x * x) + (y * y));
  return [(x / len), (y / len)];
}

function normals(x1, y1, x2, y2) {
  const dx = x2 - x1;
  const dy = y2 - y1;

  return [normalize(-dy, dx), normalize(dy, -dx)];
}

function bezierPoint(a, b, c, d, t) {
  const t1 = 1 - t;

  return (a * t1 * t1 * t1) +
    (3 * b * t * t1 * t1) +
    (3 * c * t * t * t1) +
    (d * t * t * t);
}

function bezierTangent(a, b, c, d, t) {
  return (3 * t * t * (-a + 3 * b - 3 * c + d) +
          6 * t * (a - 2 * b + c) +
          3 * (-a + b));
}

function quadraticPoint(a, b, c, t) {
  const r = ((b - a) - 0.5 * (c - a)) / (0.5 * (0.5 - 1));
  const s = c - a - r;

  return (r * t * t) + (s * t) + a;
}

function bezierCoordinates(tVals, controlPoints) {
  const xs = tVals.map(t => bezierPoint(controlPoints[0][0],
                                        controlPoints[1][0],
                                        controlPoints[2][0],
                                        controlPoints[3][0], t));
  const ys = tVals.map(t => bezierPoint(controlPoints[0][1],
                                        controlPoints[1][1],
                                        controlPoints[2][1],
                                        controlPoints[3][1], t));

  return {xs, ys};
}

function quadraticCoordinates(tVals, controlPoints) {
  const xs = tVals.map(t => quadraticPoint(controlPoints[0][0],
                                           controlPoints[1][0],
                                           controlPoints[2][0], t));
  const ys = tVals.map(t => quadraticPoint(controlPoints[0][1],
                                           controlPoints[1][1],
                                           controlPoints[2][1], t));

  return {xs, ys};
}

function distance2d([x1, y1], [x2, y2]) {
  const xd = x1 - x2;
  const yd = y1 - y2;
  return Math.sqrt((xd * xd) + (yd * yd));
}

function clamp(a, min, max) {
  return a < min ? min : (a > max ? max : a);
}

const MathUtil = {

  publicBindings: [
    new PublicBinding(
      'math/PI',
      ``,
      {},
      () => PI
    ),

    new PublicBinding(
      'math/TAU',
      ``,
      {},
      () => TAU
    ),

    new PublicBinding(
      'math/bezier-tangent',
      ``,
      {coords: [[440, 400],
                [533, 700],
                [766, 200],
                [900, 500]],
       t: 1},
      (self) => function(params) {
        const {coords, t} = self.mergeWithDefaults(params);
        let x = bezierTangent(coords[0][0],
                              coords[1][0],
                              coords[2][0],
                              coords[3][0],
                              t);
        let y = bezierTangent(coords[0][1],
                              coords[1][1],
                              coords[2][1],
                              coords[3][1],
                              t);
        return [x, y];
      }
    ),

    new PublicBinding(
      'math/sin',
      ``,
      {angle: 0},
      (self) => function(params) {
        const {angle} = self.mergeWithDefaults(params);
        return Math.sin(angle);
      }
    ),

    new PublicBinding(
      'math/cos',
      ``,
      {angle: 0},
      (self) => function(params) {
        const {angle} = self.mergeWithDefaults(params);
        return Math.cos(angle);
      }
    ),

    new PublicBinding(
      'math/atan2',
      `Calculates the arc tangent of the two variables y and x. It is similar
to calculating the arc tangent of y / x, except that the signs of
both arguments are used to determine the quadrant of the result`,
      {x: 0,
       y: 0},
      (self) => function(params) {
        const {x, y} = self.mergeWithDefaults(params);
        return Math.atan2(y, x); // this is correct, y is given before x
      }
    ),

    new PublicBinding(
      'remap-fn',
      ``,
      {},
      () => remapFn
    ),

    new PublicBinding(
      'math/distance-2d',
      ``,
      {x1: 0, y1: 0, x2: 1, y2: 1},
      (self) => function(params) {
        const {x1, y1, x2, y2} = self.mergeWithDefaults(params);
        return distance2d([x1, y1], [x2, y2]);
      }
    ),

    new PublicBinding(
      'math/clamp',
      ``,
      {val: 0, min: 0, max: 1},
      (self) => function(params) {
        const {val, min, max} = self.mergeWithDefaults(params);
        return clamp(val, min, max);
      }
    ),

    new PublicBinding(
      'degrees->radians',
      `A helper function that converts angles in degrees to radians`,
      {angle: 0.0},
      (self) => function(params) {
        const {angle} = self.mergeWithDefaults(params);
        return degreesToRadians(angle);
      }
    ),

    new PublicBinding(
      'radians->degrees',
      `A helper function that converts angles in radians to degrees`,
      {angle: 0.0},
      (self) => function(params) {
        const {angle} = self.mergeWithDefaults(params);
        return radiansToDegrees(angle);
      }
    )
  ],

  stepsInclusive: function(start, end, num) {
    const unit = (end - start) / (num - 1);
    const res = [];
    for (let i = 0; i < num; i++) {
      res.push(start + (i * unit));
    }
    return res;
  },

  distance1d: function(a, b) {
    return Math.abs(a - b);
  },

  interpolate: function(a, b, t) {
    return (a * (1 - t)) + (b * t);
  },

  mc: function([xa, ya], [xb, yb]) {
    const m = (ya - yb) / (xa - xb);
    const c = ya - (m * xa);
    return [m, c];
  },

  PI,
  TAU,
  PIbyTwo,

  degreesToRadians,
  radiansToDegrees,
  distance2d,

  remapFn,
  clamp,

  normalize,
  normals,
  bezierPoint,
  quadraticPoint,
  bezierCoordinates,
  quadraticCoordinates,

  mapLinear,
  mapQuickEase,
  mapSlowEaseIn,
  mapSlowEaseInEaseOut
};

export default MathUtil;
<commit_msg>quicker normals function<commit_after>/*
 *  Seni
 *  Copyright (C) 2015  Inderjit Gill <email@indy.io>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import PublicBinding from './PublicBinding';
import Immutable from 'immutable';

const PI = Math.PI;
const TAU = PI * 2;
const PIbyTwo = PI / 2;

const degToRad = TAU / 360;
const radToDeg = 360 / TAU;

function degreesToRadians(angle) {
  return (angle % 360) * degToRad;
}

function radiansToDegrees(angle) {
  return (angle % TAU) * radToDeg;
}

function mc([xa, ya], [xb, yb]) {
  const m = (ya - yb) / (xa - xb);
  const c = ya - (m * xa);
  return [m, c];
}

// the following map* functions work in the range 0..1:

function mapLinear(x) {
  return x;
}

function mapQuickEase(x) {
  const x2 = x * x;
  const x3 = x * x * x;
  return (3 * x2) - (2 * x3);
}

function mapSlowEaseIn(x) {
  const s = Math.sin(x * PIbyTwo);
  return s * s * s * s;
}

function mapSlowEaseInEaseOut(x) {
  return x - (Math.sin(x * TAU) / TAU);
}

const remappingFn = new Immutable.Map({'linear': mapLinear,
                                       'quick': mapQuickEase,
                                       'slow-in': mapSlowEaseIn,
                                       'slow-in-out': mapSlowEaseInEaseOut});

function remapFn(params) {

  const from = params.from || [0, 1];
  const to = params.to || [0, 100];
  const clamping = params.clamping || false;
  const mapping = params.mapping || 'linear';

  const [fromA, fromB] = from;
  const [toA, toB] = to;
  const [fromM, fromC] = mc([fromA, 0], [fromB, 1]);
  const [toM, toC] = mc([0, toA], [1, toB]);

  let normalisedMappingFn = remappingFn.get(mapping);

  if (normalisedMappingFn === undefined) {
    normalisedMappingFn = remappingFn.get('linear');
  }

  return function(parameters) {
    const val = parameters.val || 0;
    const fromInterp = (fromM * val) + fromC;
    const toInterp = normalisedMappingFn(fromInterp);
    const res = (toM * toInterp) + toC;

    if (clamping) {
      return fromInterp < 0 ? toA : (fromInterp > 1) ? toB : res;
    } else {
      return res;
    }
  };
}

function normalize(x, y) {
  const len = Math.sqrt((x * x) + (y * y));
  return [(x / len), (y / len)];
}

function normals(x1, y1, x2, y2) {
  const dx = x2 - x1;
  const dy = y2 - y1;

  const [nx, ny] = normalize(-dy, dx);
  return [[nx, ny], [-nx, -ny]];
}

function bezierPoint(a, b, c, d, t) {
  const t1 = 1 - t;

  return (a * t1 * t1 * t1) +
    (3 * b * t * t1 * t1) +
    (3 * c * t * t * t1) +
    (d * t * t * t);
}

function bezierTangent(a, b, c, d, t) {
  return (3 * t * t * (-a + 3 * b - 3 * c + d) +
          6 * t * (a - 2 * b + c) +
          3 * (-a + b));
}

function quadraticPoint(a, b, c, t) {
  const r = ((b - a) - 0.5 * (c - a)) / (0.5 * (0.5 - 1));
  const s = c - a - r;

  return (r * t * t) + (s * t) + a;
}

function bezierCoordinates(tVals, controlPoints) {
  const xs = tVals.map(t => bezierPoint(controlPoints[0][0],
                                        controlPoints[1][0],
                                        controlPoints[2][0],
                                        controlPoints[3][0], t));
  const ys = tVals.map(t => bezierPoint(controlPoints[0][1],
                                        controlPoints[1][1],
                                        controlPoints[2][1],
                                        controlPoints[3][1], t));

  return {xs, ys};
}

function quadraticCoordinates(tVals, controlPoints) {
  const xs = tVals.map(t => quadraticPoint(controlPoints[0][0],
                                           controlPoints[1][0],
                                           controlPoints[2][0], t));
  const ys = tVals.map(t => quadraticPoint(controlPoints[0][1],
                                           controlPoints[1][1],
                                           controlPoints[2][1], t));

  return {xs, ys};
}

function distance2d([x1, y1], [x2, y2]) {
  const xd = x1 - x2;
  const yd = y1 - y2;
  return Math.sqrt((xd * xd) + (yd * yd));
}

function clamp(a, min, max) {
  return a < min ? min : (a > max ? max : a);
}

const MathUtil = {

  publicBindings: [
    new PublicBinding(
      'math/PI',
      ``,
      {},
      () => PI
    ),

    new PublicBinding(
      'math/TAU',
      ``,
      {},
      () => TAU
    ),

    new PublicBinding(
      'math/bezier-tangent',
      ``,
      {coords: [[440, 400],
                [533, 700],
                [766, 200],
                [900, 500]],
       t: 1},
      (self) => function(params) {
        const {coords, t} = self.mergeWithDefaults(params);
        let x = bezierTangent(coords[0][0],
                              coords[1][0],
                              coords[2][0],
                              coords[3][0],
                              t);
        let y = bezierTangent(coords[0][1],
                              coords[1][1],
                              coords[2][1],
                              coords[3][1],
                              t);
        return [x, y];
      }
    ),

    new PublicBinding(
      'math/sin',
      ``,
      {angle: 0},
      (self) => function(params) {
        const {angle} = self.mergeWithDefaults(params);
        return Math.sin(angle);
      }
    ),

    new PublicBinding(
      'math/cos',
      ``,
      {angle: 0},
      (self) => function(params) {
        const {angle} = self.mergeWithDefaults(params);
        return Math.cos(angle);
      }
    ),

    new PublicBinding(
      'math/atan2',
      `Calculates the arc tangent of the two variables y and x. It is similar
to calculating the arc tangent of y / x, except that the signs of
both arguments are used to determine the quadrant of the result`,
      {x: 0,
       y: 0},
      (self) => function(params) {
        const {x, y} = self.mergeWithDefaults(params);
        return Math.atan2(y, x); // this is correct, y is given before x
      }
    ),

    new PublicBinding(
      'remap-fn',
      ``,
      {},
      () => remapFn
    ),

    new PublicBinding(
      'math/distance-2d',
      ``,
      {x1: 0, y1: 0, x2: 1, y2: 1},
      (self) => function(params) {
        const {x1, y1, x2, y2} = self.mergeWithDefaults(params);
        return distance2d([x1, y1], [x2, y2]);
      }
    ),

    new PublicBinding(
      'math/clamp',
      ``,
      {val: 0, min: 0, max: 1},
      (self) => function(params) {
        const {val, min, max} = self.mergeWithDefaults(params);
        return clamp(val, min, max);
      }
    ),

    new PublicBinding(
      'degrees->radians',
      `A helper function that converts angles in degrees to radians`,
      {angle: 0.0},
      (self) => function(params) {
        const {angle} = self.mergeWithDefaults(params);
        return degreesToRadians(angle);
      }
    ),

    new PublicBinding(
      'radians->degrees',
      `A helper function that converts angles in radians to degrees`,
      {angle: 0.0},
      (self) => function(params) {
        const {angle} = self.mergeWithDefaults(params);
        return radiansToDegrees(angle);
      }
    )
  ],

  stepsInclusive: function(start, end, num) {
    const unit = (end - start) / (num - 1);
    const res = [];
    for (let i = 0; i < num; i++) {
      res.push(start + (i * unit));
    }
    return res;
  },

  distance1d: function(a, b) {
    return Math.abs(a - b);
  },

  interpolate: function(a, b, t) {
    return (a * (1 - t)) + (b * t);
  },

  mc: function([xa, ya], [xb, yb]) {
    const m = (ya - yb) / (xa - xb);
    const c = ya - (m * xa);
    return [m, c];
  },

  PI,
  TAU,
  PIbyTwo,

  degreesToRadians,
  radiansToDegrees,
  distance2d,

  remapFn,
  clamp,

  normalize,
  normals,
  bezierPoint,
  quadraticPoint,
  bezierCoordinates,
  quadraticCoordinates,

  mapLinear,
  mapQuickEase,
  mapSlowEaseIn,
  mapSlowEaseInEaseOut
};

export default MathUtil;
<|endoftext|>


<commit_before>var STATE = (function(){
  var stateChangedEvents = [];

  var triggerStateChanged = function(changeType, changeDetail){
    for(var callback of stateChangedEvents){
      callback(changeType, changeDetail);
    }
  };

  var defineTriggeringProperty = function(obj, type, property){
    var name = "_"+property;

    Object.defineProperty(obj, property, {
      get: (() => obj[name]),
      set: (value => {
        obj[name] = value;
        triggerStateChanged(type, property);
      })
    });
  };

  /*
   * Internal settings class constructor.
   */
  var SETTINGS = function(){
    defineTriggeringProperty(this, "setting", "autoscroll");
  };

  /*
   * Resets settings without triggering state changed event.
   */
  SETTINGS.prototype._reset = function(){
    this._autoscroll = true;
  };

  /*
   * Internal class constructor.
   */
  var CLS = function(){
    this.settings = new SETTINGS();
    this.resetState();
  };

  /*
   * Resets the state to default values.
   */
  CLS.prototype.resetState = function(){
    this._savefile = null;
    this._isTracking = false;
    this.settings._reset();
    triggerStateChanged("data", "reset");
  };

  /*
   * Returns the savefile object, creates a new one if needed.
   */
  CLS.prototype.getSavefile = function(){
    if (!this._savefile){
      this._savefile = new SAVEFILE();
    }

    return this._savefile;
  };

  /*
   * Returns true if the database file contains any data.
   */
  CLS.prototype.hasSavedData = function(){
    return this._savefile != null;
  };

  /*
   * Returns true if currently tracking message.
   */
  CLS.prototype.isTracking = function(){
    return this._isTracking;
  };

  /*
   * Toggles the tracking state.
   */
  CLS.prototype.toggleTracking = function(){
    this._isTracking = !this._isTracking;
    triggerStateChanged("tracking", this._isTracking);
  };

  /*
   * Combines current savefile with the provided one.
   */
  CLS.prototype.uploadSavefile = function(readFile){
    this.getSavefile().combineWith(readFile);
    triggerStateChanged("data", "upload");
  };

  /*
   * Triggers a savefile download, if available.
   */
  CLS.prototype.downloadSavefile = function(){
    if (this.hasSavedData()){
      DOM.downloadTextFile("dht.txt", this._savefile.toJson());
    }
  };

  /*
   * Registers a Discord server and channel.
   */
  CLS.prototype.addDiscordChannel = function(serverName, serverType, channelId, channelName){
    var serverIndex = this.getSavefile().findOrRegisterServer(serverName, serverType);

    if (this.getSavefile().tryRegisterChannel(serverIndex, channelId, channelName) === true){
      triggerStateChanged("data", "channel");
    }
  };

  /*
   * Adds all messages from the array to the specified channel.
   */
  CLS.prototype.addDiscordMessages = function(channelId, discordMessageArray){
    if (this.getSavefile().addMessagesFromDiscord(channelId, discordMessageArray)){
      triggerStateChanged("data", "messages");
    }
  };

  /*
   * Adds a listener that is called whenever the state changes. The callback is a function that takes subject (generic type) and detail (specific type or data).
   */
  CLS.prototype.onStateChanged = function(callback){
    stateChangedEvents.push(callback);
  };

  return new CLS();
})();
<commit_msg>Add a return value to addDiscordMessages in state.js<commit_after>var STATE = (function(){
  var stateChangedEvents = [];

  var triggerStateChanged = function(changeType, changeDetail){
    for(var callback of stateChangedEvents){
      callback(changeType, changeDetail);
    }
  };

  var defineTriggeringProperty = function(obj, type, property){
    var name = "_"+property;

    Object.defineProperty(obj, property, {
      get: (() => obj[name]),
      set: (value => {
        obj[name] = value;
        triggerStateChanged(type, property);
      })
    });
  };

  /*
   * Internal settings class constructor.
   */
  var SETTINGS = function(){
    defineTriggeringProperty(this, "setting", "autoscroll");
    defineTriggeringProperty(this, "setting", "afterFirstMsg");
    defineTriggeringProperty(this, "setting", "afterSavedMsg");
  };

  /*
   * Resets settings without triggering state changed event.
   */
  SETTINGS.prototype._reset = function(){
    this._autoscroll = true;
    this._afterFirstMsg = "optPause";
    this._afterSavedMsg = "optPause";
  };

  /*
   * Internal class constructor.
   */
  var CLS = function(){
    this.settings = new SETTINGS();
    this.resetState();
  };

  /*
   * Resets the state to default values.
   */
  CLS.prototype.resetState = function(){
    this._savefile = null;
    this._isTracking = false;
    this.settings._reset();
    triggerStateChanged("data", "reset");
  };

  /*
   * Returns the savefile object, creates a new one if needed.
   */
  CLS.prototype.getSavefile = function(){
    if (!this._savefile){
      this._savefile = new SAVEFILE();
    }

    return this._savefile;
  };

  /*
   * Returns true if the database file contains any data.
   */
  CLS.prototype.hasSavedData = function(){
    return this._savefile != null;
  };

  /*
   * Returns true if currently tracking message.
   */
  CLS.prototype.isTracking = function(){
    return this._isTracking;
  };

  /*
   * Toggles the tracking state.
   */
  CLS.prototype.toggleTracking = function(){
    this._isTracking = !this._isTracking;
    triggerStateChanged("tracking", this._isTracking);
  };

  /*
   * Combines current savefile with the provided one.
   */
  CLS.prototype.uploadSavefile = function(readFile){
    this.getSavefile().combineWith(readFile);
    triggerStateChanged("data", "upload");
  };

  /*
   * Triggers a savefile download, if available.
   */
  CLS.prototype.downloadSavefile = function(){
    if (this.hasSavedData()){
      DOM.downloadTextFile("dht.txt", this._savefile.toJson());
    }
  };

  /*
   * Registers a Discord server and channel.
   */
  CLS.prototype.addDiscordChannel = function(serverName, serverType, channelId, channelName){
    var serverIndex = this.getSavefile().findOrRegisterServer(serverName, serverType);

    if (this.getSavefile().tryRegisterChannel(serverIndex, channelId, channelName) === true){
      triggerStateChanged("data", "channel");
    }
  };

  /*
   * Adds all messages from the array to the specified channel. Returns true if the savefile was updated.
   */
  CLS.prototype.addDiscordMessages = function(channelId, discordMessageArray){
    if (this.getSavefile().addMessagesFromDiscord(channelId, discordMessageArray)){
      triggerStateChanged("data", "messages");
      return true;
    }
    else{
      return false;
    }
  };

  /*
   * Adds a listener that is called whenever the state changes. The callback is a function that takes subject (generic type) and detail (specific type or data).
   */
  CLS.prototype.onStateChanged = function(callback){
    stateChangedEvents.push(callback);
  };

  return new CLS();
})();
<|endoftext|>


<commit_before>const IS_DEV = require('./is-dev');

/**
 * Log a development warning.
 * @param {String} message
 */

export default function warning(message, ...more) {
    if (!IS_DEV) {
        return;
    }

    if (typeof console !== 'undefined') {
        console.error(`Warning: ${message}`, ...more); // eslint-disable-line no-console
    }

    try {
    // --- Welcome to debugging Slate ---
    // This error was thrown as a convenience so that you can use this stack
    // to find the callsite that caused this warning to fire.
        throw new Error(message);
    } catch (x) {
    // This error is only for debugging
    }
}
<commit_msg>Fix exporting utils/warning function<commit_after>const IS_DEV = require('./is-dev');

/**
 * Log a development warning.
 * @param {String} message
 */

module.exports = function warning(message, ...more) {
    if (!IS_DEV) {
        return;
    }

    if (typeof console !== 'undefined') {
        console.error(`Warning: ${message}`, ...more); // eslint-disable-line no-console
    }

    try {
    // --- Welcome to debugging Slate ---
    // This error was thrown as a convenience so that you can use this stack
    // to find the callsite that caused this warning to fire.
        throw new Error(message);
    } catch (x) {
    // This error is only for debugging
    }
};
<|endoftext|>


<commit_before>import {Metadata, Origin} from 'aurelia-metadata';
import {relativeToFile} from 'aurelia-path';

export class ViewStrategy {
  makeRelativeTo(baseUrl){}

  loadViewFactory(viewEngine, options){
    throw new Error('A ViewStrategy must implement loadViewFactory(viewEngine, options).');
  }

  static normalize(value){
    if(typeof value === 'string'){
      value = new UseView(value);
    }

    if(value && !(value instanceof ViewStrategy)){
      throw new Error('The view must be a string or an instance of ViewStrategy.');
    }

    return value;
  }

  static getDefault(target){
    var strategy, annotation;

    if(typeof target !== 'function'){
      target = target.constructor;
    }

    annotation = Origin.get(target);
    strategy = Metadata.on(target).first(ViewStrategy);

    if(!strategy){
      if(!annotation){
        throw new Error('Cannot determinte default view strategy for object.', target);
      }

      strategy = new ConventionalViewStrategy(annotation.moduleId);
    }else if(annotation){
      strategy.moduleId = annotation.moduleId;
    }

    return strategy;
  }
}

export class UseViewStrategy extends ViewStrategy {
  constructor(path){
    super();
    this.path = path;
  }

  loadViewFactory(viewEngine, options){
    if(!this.absolutePath && this.moduleId){
      this.absolutePath = relativeToFile(this.path, this.moduleId);
    }

    return viewEngine.loadViewFactory(this.absolutePath || this.path, options, this.moduleId);
  }

  makeRelativeTo(file){
    this.absolutePath = relativeToFile(this.path, file);
  }
}

export class ConventionalViewStrategy extends ViewStrategy {
  constructor(moduleId){
    super();
    this.moduleId = moduleId;
    this.viewUrl = ConventionalViewStrategy.convertModuleIdToViewUrl(moduleId);
  }

  loadViewFactory(viewEngine, options){
    return viewEngine.loadViewFactory(this.viewUrl, options, this.moduleId);
  }

  static convertModuleIdToViewUrl(moduleId){
    return moduleId + '.html';
  }
}

export class NoViewStrategy extends ViewStrategy {
  loadViewFactory(){
    return Promise.resolve(null);
  }
}

export class TemplateRegistryViewStrategy extends ViewStrategy {
  constructor(moduleId, registryEntry){
    super();
    this.moduleId = moduleId;
    this.registryEntry = registryEntry;
  }

  loadViewFactory(viewEngine, options){
    if(this.registryEntry.isReady){
      return Promise.resolve(this.registryEntry.factory);
    }

    return viewEngine.loadViewFactory(this.registryEntry, options, this.moduleId);
  }
}
<commit_msg>fix(view-strategy): typo in view strategy normalization<commit_after>import {Metadata, Origin} from 'aurelia-metadata';
import {relativeToFile} from 'aurelia-path';

export class ViewStrategy {
  makeRelativeTo(baseUrl){}

  loadViewFactory(viewEngine, options){
    throw new Error('A ViewStrategy must implement loadViewFactory(viewEngine, options).');
  }

  static normalize(value){
    if(typeof value === 'string'){
      value = new UseViewStrategy(value);
    }

    if(value && !(value instanceof ViewStrategy)){
      throw new Error('The view must be a string or an instance of ViewStrategy.');
    }

    return value;
  }

  static getDefault(target){
    var strategy, annotation;

    if(typeof target !== 'function'){
      target = target.constructor;
    }

    annotation = Origin.get(target);
    strategy = Metadata.on(target).first(ViewStrategy);

    if(!strategy){
      if(!annotation){
        throw new Error('Cannot determinte default view strategy for object.', target);
      }

      strategy = new ConventionalViewStrategy(annotation.moduleId);
    }else if(annotation){
      strategy.moduleId = annotation.moduleId;
    }

    return strategy;
  }
}

export class UseViewStrategy extends ViewStrategy {
  constructor(path){
    super();
    this.path = path;
  }

  loadViewFactory(viewEngine, options){
    if(!this.absolutePath && this.moduleId){
      this.absolutePath = relativeToFile(this.path, this.moduleId);
    }

    return viewEngine.loadViewFactory(this.absolutePath || this.path, options, this.moduleId);
  }

  makeRelativeTo(file){
    this.absolutePath = relativeToFile(this.path, file);
  }
}

export class ConventionalViewStrategy extends ViewStrategy {
  constructor(moduleId){
    super();
    this.moduleId = moduleId;
    this.viewUrl = ConventionalViewStrategy.convertModuleIdToViewUrl(moduleId);
  }

  loadViewFactory(viewEngine, options){
    return viewEngine.loadViewFactory(this.viewUrl, options, this.moduleId);
  }

  static convertModuleIdToViewUrl(moduleId){
    return moduleId + '.html';
  }
}

export class NoViewStrategy extends ViewStrategy {
  loadViewFactory(){
    return Promise.resolve(null);
  }
}

export class TemplateRegistryViewStrategy extends ViewStrategy {
  constructor(moduleId, registryEntry){
    super();
    this.moduleId = moduleId;
    this.registryEntry = registryEntry;
  }

  loadViewFactory(viewEngine, options){
    if(this.registryEntry.isReady){
      return Promise.resolve(this.registryEntry.factory);
    }

    return viewEngine.loadViewFactory(this.registryEntry, options, this.moduleId);
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import CSSCore from 'fbjs/lib/CSSCore';
import Container from './container';

class ContainerManager {

  constructor() {
    this.cache = {};
  }

  ensure(id, shouldAppend) {
    let container = this.cache[id];

    if (!container) {
      container = this.cache[id] = global.document.getElementById(id);
    }

    if (!container && shouldAppend) {
      container = global.document.createElement('div');
      container.id = id;
      container.className = "auth0-lock-container";
      this.cache[id] = global.document.body.appendChild(container);
    }

    if (!container) {
      throw new Error(`Can't find element with id ${id}`);
    }

    return container;
  }

}

class Renderer {

  constructor() {
    this.containerManager = new ContainerManager();
    this.modals = {};
  }

  render(props, containerId, isModal) {
    // TODO: take containerId and isModal from props.
    const container = this.containerManager.ensure(containerId, isModal);

    if (isModal && !this.modals[containerId]) {
      CSSCore.addClass(
        global.document.getElementsByTagName("html")[0],
        "auth0-lock-html"
      );
    }

    const component = ReactDOM.render(<Container {...props} />, container);

    if (isModal) {
      this.modals[containerId] = component;
    }

    return component;
  }

  remove(containerId) {
    if (this.modals[containerId]) {
      this.modals[containerId].hide();
      setTimeout(() => this.unmount(containerId), 1000);
    } else {
      this.unmount(containerId);
    }
  }

  unmount(containerId) {
    try {
      const container = this.containerManager.ensure(containerId);
      ReactDOM.unmountComponentAtNode(container);
    } catch (e) {
      // do nothing if container doesn't exist
    }

    if (this.modals[containerId]) {
      delete this.modals[containerId];

      CSSCore.removeClass(
        global.document.getElementsByTagName("html")[0],
        "auth0-lock-html"
      );
    }

  }
}

const renderer = new Renderer();

export const render = (...args) => renderer.render(...args);
export const remove = (...args) => renderer.remove(...args);
<commit_msg>Don't unmount component if it is not mounted<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import CSSCore from 'fbjs/lib/CSSCore';
import Container from './container';

class ContainerManager {

  constructor() {
    this.cache = {};
  }

  ensure(id, shouldAppend) {
    let container = this.cache[id];

    if (!container) {
      container = this.cache[id] = global.document.getElementById(id);
    }

    if (!container && shouldAppend) {
      container = global.document.createElement('div');
      container.id = id;
      container.className = "auth0-lock-container";
      this.cache[id] = global.document.body.appendChild(container);
    }

    if (!container) {
      throw new Error(`Can't find element with id ${id}`);
    }

    return container;
  }

}

class Renderer {

  constructor() {
    this.containerManager = new ContainerManager();
    this.modals = {};
  }

  render(props, containerId, isModal) {
    // TODO: take containerId and isModal from props.
    const container = this.containerManager.ensure(containerId, isModal);

    if (isModal && !this.modals[containerId]) {
      CSSCore.addClass(
        global.document.getElementsByTagName("html")[0],
        "auth0-lock-html"
      );
    }

    const component = ReactDOM.render(<Container {...props} />, container);

    if (isModal) {
      this.modals[containerId] = component;
    }

    return component;
  }

  remove(containerId) {
    if (this.modals[containerId]) {
      this.modals[containerId].hide();
      setTimeout(() => this.unmount(containerId), 1000);
    } else {
      this.unmount(containerId);
    }
  }

  unmount(containerId) {
    try {
      const container = this.containerManager.ensure(containerId);
      if (container) {
        ReactDOM.unmountComponentAtNode(container);
      }
    } catch (e) {
      // do nothing if container doesn't exist
    }

    if (this.modals[containerId]) {
      delete this.modals[containerId];

      CSSCore.removeClass(
        global.document.getElementsByTagName("html")[0],
        "auth0-lock-html"
      );
    }

  }
}

const renderer = new Renderer();

export const render = (...args) => renderer.render(...args);
export const remove = (...args) => renderer.remove(...args);
<|endoftext|>


<commit_before>const buildfire = require('buildfire');
const { formatSSO } = require('./formatSSO');

const viewOptions = {
  POPUP: 'In app popup',
  NATIVE: 'Native In App',
  EXTERNAL: 'External browser'
};

const flags = {};

const setFlags = (content) => {
  flags.isWeb = (buildfire.context.device.platform == 'web');
  flags.shouldOpenInApp = (content.view == viewOptions.NATIVE);
  flags.isNotCP = (flags.isLiveMode || !flags.isWeb);
  flags.isLiveMode = buildfire.context.liveMode;
  flags.requiresSSO = content.url && content.url.indexOf('{{SSO}}') > 0;
  flags.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
};

const render = (content) => {
  setFlags(content);
  const displayIniFrame = flags.isNotCP && flags.shouldOpenInApp;
  const openWindow = flags.isNotCP && !flags.shouldOpenInApp;
  const displaySuccessMessage = content.url && flags.isWeb && !flags.isLiveMode;

  if(displayIniFrame){
    renderiFrame({url: content.url, isIOS: flags.isIOS});
    return;
  }

  if (flags.requiresSSO) {
    buildfire.auth.getCurrentUser((err, result) => {
      if (result && result.SSO && result.SSO.accessToken) {
        content.url = formatSSO(content.url, JSON.stringify(result.SSO));
        window.document.getElementById('targetUrl').href = content.url;
      }
    });
  }

  if(openWindow){
    if(content.view === viewOptions.POPUP)
      buildfire.navigation.openWindow(content.url, "_blank");
    else
      buildfire.navigation.openWindow(content.url, "_system");

    setTimeout(() => buildfire.navigation.goBack(), 750);
    return;
  }

  if(displaySuccessMessage){
    window.document.getElementById('successMessage').style.display = 'block';
    window.document.getElementById('targetUrl').href = content.url;
    return;
  }
};

const renderiFrame = (props) =>{
  let currentIframe = window.document.getElementById('webviewIframe');
  if (currentIframe) {
    currentIframe.remove();
  }

  let scrollable = window.document.getElementById('scrollable');
  if (!scrollable && props.isIOS) {
    window.document.body.appendChild((() => {
      let div = document.createElement('div');
      div.id = 'scrollable';
      div.className = 'scrollable';
      scrollable = div;
      return div;
    })());
  }

  window.document.body.appendChild((() => {
    let p = window.document.createElement('p');
    p.innerHTML = 'Loading...';
    p.className = 'bodyTextTheme backgroundColorTheme';
    p.style.position = 'absolute';
    p.style.top = 0;
    p.style.padding = '8px 0';
    p.style.display = 'inline-block';
    p.style.width = '100%';
    p.style.left= 0;
    p.style.background = '#eef0f0';
    p.style.textAlign = 'center';
    p.style.color = '#5f5f5f';
    p.id = 'loadingText';
    return p;
  })());

  let container = props.isIOS ? scrollable : window.document.body;

  container.appendChild((() => {
    let iFrame = window.document.createElement('iframe');
    iFrame.id = 'webviewIframe';
    iFrame.src = props.url;
    iFrame.scrolling = props.isIOS ? 'no' : 'auto';
    iFrame.style.height = '100%';
    iFrame.style.width = '1px';
    iFrame.style.minWidth = '100%';
    iFrame.onload = () => {
      window.document.getElementById('loadingText').remove();
    };
    return iFrame;
  })());
};

buildfire.spinner.show();
buildfire.datastore.onUpdate(event => render(event.data.content));
buildfire.datastore.get((err, result) => {
  if (err) {
    console.error("error: ", err);
    buildfire.spinner.hide();
    return;
  }

  if(!result.data || !result.data.content){
    buildfire.spinner.hide();
    return;
  }

  const { content } = result.data;

  render(content);

  buildfire.spinner.hide();

  try {
    buildfire.appearance.ready();
  } catch (err) {
    console.log('appearance.ready() failed. Is sdk up to date?');
  }
});
<commit_msg>Final touches on the mobile side.<commit_after>const buildfire = require('buildfire');
const { formatSSO } = require('./formatSSO');

const viewOptions = {
  POPUP: 'In app popup',
  NATIVE: 'Native In App',
  EXTERNAL: 'External browser'
};

const flags = {};

const setFlags = (content) => {
  flags.isWeb = (buildfire.context.device.platform == 'web');
  flags.shouldOpenInApp = (content.view == viewOptions.NATIVE);
  flags.isNotCP = (flags.isLiveMode || !flags.isWeb);
  flags.isLiveMode = buildfire.context.liveMode;
  flags.requiresSSO = content.url && content.url.indexOf('{{SSO}}') > 0;
  flags.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
};

const render = (content) => {

  const handleWindow = (openWindow, displayIniFrame, displaySuccessMessage) => {
    if(openWindow){
      if(content.view === viewOptions.POPUP)
        buildfire.navigation.openWindow(content.url, "_blank");
      else
        buildfire.navigation.openWindow(content.url, "_system");

      setTimeout(() => buildfire.navigation.goBack(), 750);
      return;
    }
    if(displayIniFrame){
      renderiFrame({url: content.url, isIOS: flags.isIOS});
      return;
    }
    if(displaySuccessMessage){
      window.document.getElementById('successMessage').style.display = 'block';
      window.document.getElementById('targetUrl').href = content.url;
      return;
    }
  };

  setFlags(content);
  const displayIniFrame = flags.isNotCP && flags.shouldOpenInApp;  //on the device and open native
  const openWindow = flags.isNotCP && !flags.shouldOpenInApp;      //on the device and open in pop up or native brow
  const displaySuccessMessage = content.url && flags.isWeb && !flags.isLiveMode;

  if (flags.requiresSSO) {   //This is an SSO webview with an access token
    buildfire.auth.getCurrentUser((err, result) => {
      if (result && result.SSO && result.SSO.accessToken) {
        content.url = formatSSO(content.url, JSON.stringify(result.SSO));
        window.document.getElementById('targetUrl').href = content.url;  //to display the URL in the CP
        handleWindow(openWindow, displayIniFrame, displaySuccessMessage);
      }
    });
  } else {   //this is all other URLs, i.e. no SSO.
    handleWindow(openWindow, displayIniFrame, displaySuccessMessage);
  }

};

const renderiFrame = (props) =>{
  let currentIframe = window.document.getElementById('webviewIframe');
  if (currentIframe) {
    currentIframe.remove();
  }

  let scrollable = window.document.getElementById('scrollable');
  if (!scrollable && props.isIOS) {
    window.document.body.appendChild((() => {
      let div = document.createElement('div');
      div.id = 'scrollable';
      div.className = 'scrollable';
      scrollable = div;
      return div;
    })());
  }

  window.document.body.appendChild((() => {
    let p = window.document.createElement('p');
    p.innerHTML = 'Loading...';
    p.className = 'bodyTextTheme backgroundColorTheme';
    p.style.position = 'absolute';
    p.style.top = 0;
    p.style.padding = '8px 0';
    p.style.display = 'inline-block';
    p.style.width = '100%';
    p.style.left= 0;
    p.style.background = '#eef0f0';
    p.style.textAlign = 'center';
    p.style.color = '#5f5f5f';
    p.id = 'loadingText';
    return p;
  })());

  let container = props.isIOS ? scrollable : window.document.body;

  container.appendChild((() => {
    let iFrame = window.document.createElement('iframe');
    iFrame.id = 'webviewIframe';
    iFrame.src = props.url;
    iFrame.scrolling = props.isIOS ? 'no' : 'auto';
    iFrame.style.height = '100%';
    iFrame.style.width = '1px';
    iFrame.style.minWidth = '100%';
    iFrame.onload = () => {
      window.document.getElementById('loadingText').remove();
    };
    return iFrame;
  })());
};

buildfire.spinner.show();
buildfire.datastore.onUpdate(event => render(event.data.content));
buildfire.datastore.get((err, result) => {
  if (err) {
    console.error("error: ", err);
    buildfire.spinner.hide();
    return;
  }

  if(!result.data || !result.data.content){
    buildfire.spinner.hide();
    return;
  }

  const { content } = result.data;

  render(content);

  buildfire.spinner.hide();

  try {
    buildfire.appearance.ready();
  } catch (err) {
    console.log('appearance.ready() failed. Is sdk up to date?');
  }
});
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');

module.exports = () => {
    const obj = {};

    fs.readdirSync(__dirname).forEach((name) => {
        if (name.endsWith('.js') && name != 'index.js') {
            obj[name.substring(0, name.indexOf('.'))] = require(path.join(__dirname, name));
        }
    });

    fs.readdirSync(path.join(__dirname, '..', 'modules')).forEach((name) => {
        obj[name] = require(path.join(__dirname, '..', 'modules', name, 'languages'));
    });

    console.log(obj);

    return obj;
};<commit_msg>update<commit_after>'use strict';

const fs = require('fs');
const path = require('path');

module.exports = () => {
    const obj = {};

    fs.readdirSync(__dirname).forEach((name) => {
        if (name.endsWith('.js') && name != 'index.js') {
            obj[name.substring(0, name.indexOf('.'))] = require(path.join(__dirname, name));
        }
    });

    fs.readdirSync(path.join(__dirname, '..', 'modules')).forEach((name) => {
        obj[name] = require(path.join(__dirname, '..', 'modules', name, 'languages'));
    });

    return obj;
};<|endoftext|>


<commit_before>exports.Spinner = function() {
    this.cnt = 0;
    this.interval = null;

    this.spin = function() {
        process.stdout.cursorTo(0);
        switch (this.cnt) {
            case 0:
                process.stdout.write('/');
                break;
            case 1:
                process.stdout.write('-');
                break;
            case 2:
                process.stdout.write("\\");
                break;
            case 3:
                process.stdout.write('|');
                break;
        }
        this.cnt++;
        this.cnt = (this.cnt > 3) ? 0 : this.cnt;
    };

    this.start = function() {
        this.spin();
        this.interval = setInterval(this.spin, 50);
    };

    this.stop = function(){
        if (this.interval) {
            clearInterval(this.interval);
        }
        process.stdout.clearLine();
        process.stdout.cursorTo(0);
    };
}
<commit_msg>spacing in spinner<commit_after>exports.Spinner = function() {
    this.cnt = 0;
    this.interval = null;

    this.spin = function() {
        process.stdout.cursorTo(0);
        switch (this.cnt) {
            case 0:
                process.stdout.write('  /  ');
                break;
            case 1:
                process.stdout.write('  -  ');
                break;
            case 2:
                process.stdout.write("  \\  ");
                break;
            case 3:
                process.stdout.write('  |  ');
                break;
        }
        this.cnt++;
        this.cnt = (this.cnt > 3) ? 0 : this.cnt;
    };

    this.start = function() {
        this.spin();
        this.interval = setInterval(this.spin, 50);
    };

    this.stop = function(){
        if (this.interval) {
            clearInterval(this.interval);
        }
        process.stdout.clearLine();
        process.stdout.cursorTo(0);
    };
}
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require turbolinks
//= require jquery3
//= require jquery_ujs
//= require foundation
//= require_tree .

$(document).on('turbolinks:load', function() {
  $(document).foundation();
  $('body').on('click', '[data-open="ajax-reveal"]', function(e) {
    e.preventDefault();
    $.ajax({
      url: $(this).data('url'),
      success: function(result) {
        $(".reveal-content").html(result);
      },
      error: function(result) {
        $(".reveal-content").html('Error loading content. Please close popup and retry.');
      }
    });
  });
});
<commit_msg>Use single quotes instead of double quotes<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require turbolinks
//= require jquery3
//= require jquery_ujs
//= require foundation
//= require_tree .

$(document).on('turbolinks:load', function() {
  $(document).foundation();
  $('body').on('click', '[data-open="ajax-reveal"]', function(e) {
    e.preventDefault();
    $.ajax({
      url: $(this).data('url'),
      success: function(result) {
        $('.reveal-content').html(result);
      },
      error: function(result) {
        $('.reveal-content').html('Error loading content. Please close popup and retry.');
      }
    });
  });
});
<|endoftext|>


<commit_before>import * as vec2 from "../../gl-matrix/src/gl-matrix/vec2.js";

export default class Vector2 extends Float32Array {
  constructor(array = [0, 0]) {
    super(array);
    return this;
  }

  get x() {
    return this[0];
  }

  set x(value) {
    this[0] = value;
  }

  get y() {
    return this[1];
  }

  set y(value) {
    this[1] = value;
  }

  set(x, y) {
    vec2.set(this, x, y);
    return this;
  }

  copy(vector2) {
    vec2.copy(this, vector2);
    return this;
  }

  add(vector2) {
    vec2.add(this, this, vector2);
    return this;
  }

  get size() {
    return vec2.length(this);
  }

  get squaredSize() {
    return vec2.squaredLength(this);
  }

  subtract(vector2) {
    vec2.subtract(this, this, vector2);
    return this;
  }

  negate(vector2 = this) {
    vec2.negate(this, vector2);
    return this;
  }

  cross(vector2a, vector2b) {
    vec2.cross(this, vector2a, vector2b);
    return this;
  }

  scale(value) {
    vec2.scale(this, this, value);
    return this;
  }

  normalize() {
    vec2.normalize(this, this);
  }

  dot(vector2) {
    return vec2.dot(this, vector2);
  }

  equals(vector2) {
    return vec2.exactEquals(this, vector2);
  }

  applyMatrix3(matrix3) {
    vec2.transformMat3(this, this, matrix3);
    return this;
  }

  applyMatrix4(matrix4) {
    vec2.transformMat4(this, this, matrix4);
    return this;
  }

  lerp(vector2, value) {
    vec2.lerp(this, this, vector2, value);
  }

  clone() {
    return new Vector2(this);
  }
}
<commit_msg>add distance to Vector2<commit_after>import * as vec2 from "../../gl-matrix/src/gl-matrix/vec2.js";

export default class Vector2 extends Float32Array {
  static distance(vector2a, vector2b) {
    return vec2.distance(vector2a, vector2b);
  }

  constructor(array = [0, 0]) {
    super(array);
    return this;
  }

  get x() {
    return this[0];
  }

  set x(value) {
    this[0] = value;
  }

  get y() {
    return this[1];
  }

  set y(value) {
    this[1] = value;
  }

  set(x, y) {
    vec2.set(this, x, y);
    return this;
  }

  copy(vector2) {
    vec2.copy(this, vector2);
    return this;
  }

  add(vector2) {
    vec2.add(this, this, vector2);
    return this;
  }

  get size() {
    return vec2.length(this);
  }

  get squaredSize() {
    return vec2.squaredLength(this);
  }

  subtract(vector2) {
    vec2.subtract(this, this, vector2);
    return this;
  }

  negate(vector2 = this) {
    vec2.negate(this, vector2);
    return this;
  }

  cross(vector2a, vector2b) {
    vec2.cross(this, vector2a, vector2b);
    return this;
  }

  scale(value) {
    vec2.scale(this, this, value);
    return this;
  }

  normalize() {
    vec2.normalize(this, this);
  }

  dot(vector2) {
    return vec2.dot(this, vector2);
  }

  distance(vector2) {
    return Vector2.distance(this, vector2);
  }

  equals(vector2) {
    return vec2.exactEquals(this, vector2);
  }

  applyMatrix3(matrix3) {
    vec2.transformMat3(this, this, matrix3);
    return this;
  }

  applyMatrix4(matrix4) {
    vec2.transformMat4(this, this, matrix4);
    return this;
  }

  lerp(vector2, value) {
    vec2.lerp(this, this, vector2, value);
  }

  clone() {
    return new Vector2(this);
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
/*
 * ./maptail.js <file_to_tail> [hostname] [port]
 */

var http = require('http')
  , spawn = require('child_process').spawn

  , express = require('express')

  , login = require('helpful/login')
  , allow = require('helpful/allow')
  , boil = require('helpful/boil')
  , helpful = require('helpful')

  , io = require('socket.io')

  , geoip = require('geoip')
  , cities = geoip.open(__dirname + '/GeoLiteCity.dat')

  , log = helpful.log(1)
  , start = helpful.start
  , rewrite = helpful.rewrite
  , cache = helpful.cache
  , compile = helpful.compile
  , render = helpful.render
  , expires = helpful.expires

  , port = process.argv.length >= 4 && process.argv[4] || process.env.PORT || process.env.POLLA_PORT || 8080
  , host = process.argv.length >= 3 && process.argv[3] || process.env.HOST || process.env.POLLA_HOST || 'localhost'

  , wsport = process.env.WSPORT || port + 111
  , wshost = process.env.WSHOST || host

  , filename = process.argv.length >= 2 && process.argv[2] || 'nohup.out'

var app = express.createServer()

var config = boil(app, {
  host: host
, 'public': __dirname + '/public'
, views: __dirname + '/views'

, context: {
    wshost: wshost
  , wsport: wsport
  , htmlspecialchars: helpful.htmlspecialchars
  }

, rewrite: function(app) {
    app.get('*.html|*.htm', rewrite(helpful.stripExt))
    app.get('/index', rewrite('/'))
  }

, custom: [
    cache()
  , login({
      css: '/css/wargames.css'
    , after: function(req, res, next) {
        req.expireAll()
        next()
      }
    })
  ]
})

compile(config.views)

// main app

var allowedIPs = {}
  , connected = {}
  , users = {}

app.get('/', expires(false), function(req, res) {
  res.render('index', { title: 'Home' })
})

// remove the allow() middleware to allow everyone in
app.get('/map', allow('admin', 'moderator'), expires(false), function(req, res) {
  allowedIPs[req.headers.ip] = req.session.user
  res.render('map', { layout: 'empty', title: 'tail -f ' + filename })
})

app.get('/js/config.js', allow('admin', 'moderator'), expires(false), function(req, res) {
  html = [
    'var WSHOST = "' + wshost + '";'
  + 'var WSPORT = ' + wsport + ';'
  ]

  res.send(html.join('\n'))
})

app.get('/admin', expires(false), function(req, res) {
  req.users.list(function(err, data) {
    res.render('admin', { title: 'Admin area', data: data })
  })
})

start(app, port, host)

// socket.io

var wsserver = http.createServer()
start(wsserver, wsport, wshost)
var socket = io.listen(wsserver)

socket.on('connection', function(client) {
  var id = client.sessionId
    , ip = client && client.request && client.request.socket && client.request.socket.remoteAddress || '000'

  if (typeof allowedIPs[ip] === 'undefined') return

  connected[id] = client

  world.sendStartupData(client)

  client.on('disconnect', function() {
    delete connected[id]
  })
})

var world = {
  users: {}
, messages: []
, messageCount: 0
, getPublicUserInfo: function(user) {
    return {
      'name': user.name
    , 'country': user.city.country_name
    , 'lng': user.lng
    , 'lat': user.lat
    , 'lastActivity': user.lastActivity
    }
  }

, sendUpdate: function(from, silent) {
    var self = this

    self.messages.push({
	    'user': from.name
    , 'country': from.city.country_name
	  , 'message': from.line
		})

		if (self.messages.length > 10) {
			self.messages.shift()
		}

    if (from && from.lat) {
      var returnObj = {
        'action': 'newMessage'
      , 'from': self.getPublicUserInfo(from)
      , 'messageCount': self.messageCount
      }

      if (!silent) {
        if (self.messages.length) {
          returnObj.messages = [self.messages[self.messages.length - 1]]
        }
        else {
          returnObj.messages = []
        }
      }

      broadcast(returnObj)
    }
    else {
      log('Failed update, ' + from.name + ' lacks coordinates.')
    }
  }

, sendStartupData: function (client) {
    var self = this

    var activityLimit = new Date().getTime() - 2400000
    var userList = {}

    Object.keys(self.users).forEach(function (name) {
      var user = self.users[name]
      if (user.lat && user.lastActivity > activityLimit) {
        userList[name] = self.getPublicUserInfo(user)
      }
    })

    client.send(JSON.stringify({
      'action': 'getUsers'
    , 'users': userList
    , 'removeTimeout': 2400000
    , 'channel': filename
    , 'messages': self.messages
    , 'messageCount': self.messageCount
    , 'serverTime': new Date().getTime()
    }))
  }
}

// tail -n 1000 -f filename

tail = spawn('tail', ['-n', '1000', '-f', filename])

tail.stdout.on('data', function (data) {
  var dataStr = data.toString()

  dataStr.replace(/([^\n]+)\n/g, function(m, line) {
    // lame ip regexp please someone patch this
    var ipsArray = line.match(/([0-2]?[0-9]{1,2}\.[0-2]?[0-9]{1,2}\.[0-2]?[0-9]{1,2}\.[0-2]?[0-9]{1,2})/gm) || []
      , ips = {}

    ipsArray.forEach(function(ip) {
      ips[ip] = { name: ip, line: helpful.htmlspecialchars(line), lastActivity: Date.now() }
    })

    for (var ip in ips) {
      // Return an object of city information
      // {
      //  "country_code":"US",
      //  "country_code3":"USA",
      //  "country_name":"United States",
      //  "continet_code":"NA",
      //  "region":"CA",
      //  "city":"Mountain View",
      //  "postal_code":"94043",
      //  "latitude":37.41919999999999,
      //  "longitude":-122.0574,
      //  "dma_code":807,
      //  "metro_code":807,
      //  "area_code":650
      //  }
      city = geoip.City.record_by_addr(cities, ip)
      if (typeof city === 'object') {
        ips[ip].city = city
        ips[ip].lat = city.latitude
        ips[ip].lng = city.longitude

        world.messageCount++
        world.users[ip] = ips[ip]

        world.sendUpdate(ips[ip])
      } else {
        log('Could not grab location for', ip, ' - ', city)
      }
    }
  })
})

tail.stderr.on('data', function (data) {
  console.log('tail stderr: ' + data)
})

tail.on('exit', function (code) {
  if (code !== 0) {
    console.log('tail process exited with code ' + code);
  }
})

// functions

var broadcast = function(msg) {
  var json = JSON.stringify(msg)

  for (var id in connected) {
    connected[id].send(json)
  }
}
<commit_msg>Minor bugfix - need a better regexp<commit_after>#!/usr/bin/env node
/*
 * ./maptail.js <file_to_tail> [hostname] [port]
 */

var http = require('http')
  , spawn = require('child_process').spawn

  , express = require('express')

  , login = require('helpful/login')
  , allow = require('helpful/allow')
  , boil = require('helpful/boil')
  , helpful = require('helpful')

  , io = require('socket.io')

  , geoip = require('geoip')
  , cities = geoip.open(__dirname + '/GeoLiteCity.dat')

  , log = helpful.log(1)
  , start = helpful.start
  , rewrite = helpful.rewrite
  , cache = helpful.cache
  , compile = helpful.compile
  , render = helpful.render
  , expires = helpful.expires

  , port = process.argv.length >= 4 && process.argv[4] || process.env.PORT || process.env.POLLA_PORT || 8080
  , host = process.argv.length >= 3 && process.argv[3] || process.env.HOST || process.env.POLLA_HOST || 'localhost'

  , wsport = process.env.WSPORT || port + 111
  , wshost = process.env.WSHOST || host

  , filename = process.argv.length >= 2 && process.argv[2] || 'nohup.out'

var app = express.createServer()

var config = boil(app, {
  host: host
, 'public': __dirname + '/public'
, views: __dirname + '/views'

, context: {
    wshost: wshost
  , wsport: wsport
  , htmlspecialchars: helpful.htmlspecialchars
  }

, rewrite: function(app) {
    app.get('*.html|*.htm', rewrite(helpful.stripExt))
    app.get('/index', rewrite('/'))
  }

, custom: [
    cache()
  , login({
      css: '/css/wargames.css'
    , after: function(req, res, next) {
        req.expireAll()
        next()
      }
    })
  ]
})

compile(config.views)

// main app

var allowedIPs = {}
  , connected = {}
  , users = {}

app.get('/', expires(false), function(req, res) {
  res.render('index', { title: 'Home' })
})

// remove the allow() middleware to allow everyone in
app.get('/map', allow('admin', 'moderator'), expires(false), function(req, res) {
  allowedIPs[req.headers.ip] = req.session.user
  res.render('map', { layout: 'empty', title: 'tail -f ' + filename })
})

app.get('/js/config.js', allow('admin', 'moderator'), expires(false), function(req, res) {
  html = [
    'var WSHOST = "' + wshost + '";'
  + 'var WSPORT = ' + wsport + ';'
  ]

  res.send(html.join('\n'))
})

app.get('/admin', expires(false), function(req, res) {
  req.users.list(function(err, data) {
    res.render('admin', { title: 'Admin area', data: data })
  })
})

start(app, port, host)

// socket.io

var wsserver = http.createServer()
start(wsserver, wsport, wshost)
var socket = io.listen(wsserver)

socket.on('connection', function(client) {
  var id = client.sessionId
    , ip = client && client.request && client.request.socket && client.request.socket.remoteAddress || '000'

  if (typeof allowedIPs[ip] === 'undefined') return

  connected[id] = client

  world.sendStartupData(client)

  client.on('disconnect', function() {
    delete connected[id]
  })
})

var world = {
  users: {}
, messages: []
, messageCount: 0
, getPublicUserInfo: function(user) {
    return {
      'name': user.name
    , 'country': user.city.country_name
    , 'lng': user.lng
    , 'lat': user.lat
    , 'lastActivity': user.lastActivity
    }
  }

, sendUpdate: function(from, silent) {
    var self = this

    self.messages.push({
	    'user': from.name
    , 'country': from.city.country_name
	  , 'message': from.line
		})

		if (self.messages.length > 10) {
			self.messages.shift()
		}

    if (from && from.lat) {
      var returnObj = {
        'action': 'newMessage'
      , 'from': self.getPublicUserInfo(from)
      , 'messageCount': self.messageCount
      }

      if (!silent) {
        if (self.messages.length) {
          returnObj.messages = [self.messages[self.messages.length - 1]]
        }
        else {
          returnObj.messages = []
        }
      }

      broadcast(returnObj)
    }
    else {
      log('Failed update, ' + from.name + ' lacks coordinates.')
    }
  }

, sendStartupData: function (client) {
    var self = this

    var activityLimit = new Date().getTime() - 2400000
    var userList = {}

    Object.keys(self.users).forEach(function (name) {
      var user = self.users[name]
      if (user.lat && user.lastActivity > activityLimit) {
        userList[name] = self.getPublicUserInfo(user)
      }
    })

    client.send(JSON.stringify({
      'action': 'getUsers'
    , 'users': userList
    , 'removeTimeout': 2400000
    , 'channel': filename
    , 'messages': self.messages
    , 'messageCount': self.messageCount
    , 'serverTime': new Date().getTime()
    }))
  }
}

// tail -n 1000 -f filename

tail = spawn('tail', ['-n', '1000', '-f', filename])

tail.stdout.on('data', function (data) {
  var dataStr = data.toString()

  dataStr.replace(/([^\n]+)\n/g, function(m, line) {
    // lame ip regexp please someone patch this
    var ipsArray = line.match(/([0-2]?[0-9]{1,2}\.[0-2]?[0-9]{1,2}\.[0-2]?[0-9]{1,2}\.[0-2]?[0-9]{1,2})/gm) || []
      , ips = {}

    ipsArray.forEach(function(ip) {
      ips[ip] = { name: ip, line: helpful.htmlspecialchars(line), lastActivity: Date.now() }
    })

    for (var ip in ips) {
      // Return an object of city information
      // {
      //  "country_code":"US",
      //  "country_code3":"USA",
      //  "country_name":"United States",
      //  "continet_code":"NA",
      //  "region":"CA",
      //  "city":"Mountain View",
      //  "postal_code":"94043",
      //  "latitude":37.41919999999999,
      //  "longitude":-122.0574,
      //  "dma_code":807,
      //  "metro_code":807,
      //  "area_code":650
      //  }
      city = geoip.City.record_by_addr(cities, ip)
      if (city) {
        ips[ip].city = city
        ips[ip].lat = city.latitude
        ips[ip].lng = city.longitude

        world.messageCount++
        world.users[ip] = ips[ip]

        world.sendUpdate(ips[ip])
      } else {
        log('Could not grab location for', ip, ' - ', city)
      }
    }
  })
})

tail.stderr.on('data', function (data) {
  console.log('tail stderr: ' + data)
})

tail.on('exit', function (code) {
  if (code !== 0) {
    console.log('tail process exited with code ' + code);
  }
})

// functions

var broadcast = function(msg) {
  var json = JSON.stringify(msg)

  for (var id in connected) {
    connected[id].send(json)
  }
}
<|endoftext|>