<commit_before>'use strict';

var _ = require('lodash')
  , utils = require('./utils')
  , objection = require('objection')
  , graphqlRoot = require('graphql')
  , jsonSchemaUtils = require('./jsonSchema')
  , defaultArgFactories = require('./argFactories')
  , GraphQLObjectType = graphqlRoot.GraphQLObjectType
  , GraphQLSchema = graphqlRoot.GraphQLSchema
  , GraphQLList = graphqlRoot.GraphQLList;

// Default arguments that are excluded from the relation arguments.
const OMIT_FROM_RELATION_ARGS = [
  // We cannot use `range` in the relation arguments since the relations are fetched
  // for multiple objects at a time. Limiting the result set would limit the combined
  // result, and not the individual model's relation.
  "range"
];

const GRAPHQL_META_FIELDS = [
  "__typename"
];

// GraphQL AST node types.
const KIND_FRAGMENT_SPREAD = 'FragmentSpread';
const KIND_VARIABLE = 'Variable';

class SchemaBuilder {

  constructor() {
    this.models = {};
    this.typeCache = {};
    this.filterIndex = 1;
    this.argFactories = [];
    this.enableSelectFiltering = true;
    this.defaultArgNameMap = {
      "eq": 'Eq',
      "gt": 'Gt',
      "gte": 'Gte',
      "lt": 'Lt',
      "lte": 'Lte',
      "like": 'Like',
      "isNull": 'IsNull',
      "likeNoCase": 'LikeNoCase',
      "in": 'In',
      "notIn": 'NotIn',
      "orderBy": 'orderBy',
      "orderByDesc": 'orderByDesc',
      "range": "range"
    };
  }

  model(modelClass, opt) {
    opt = opt || {};

    if (!modelClass.jsonSchema) {
      throw new Error('modelClass must have a jsonSchema');
    }

    this.models[modelClass.tableName] = {
      modelClass: modelClass,
      fields: null,
      args: null,
      opt: opt
    };

    return this;
  };

  defaultArgNames(defaultArgNameMap) {
    this.defaultArgNameMap = Object.assign({}, this.defaultArgNameMap, defaultArgNameMap);
    return this;
  }

  argFactory(argFactory) {
    this.argFactories.push(argFactory);
    return this;
  }

  selectFiltering(enable) {
    this.enableSelectFiltering = !!enable;
    return this;
  }

  build() {
    _.forOwn(this.models, modelData => {
      modelData.fields = jsonSchemaUtils.jsonSchemaToGraphQLFields(modelData.modelClass.jsonSchema, {
        include: modelData.opt.include,
        exclude: modelData.opt.exclude,
        typeNamePrefix: utils.typeNameForModel(modelData.modelClass),
        typeCache: this.typeCache
      });

      modelData.args = this._argsForModel(modelData);
    });

    return new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'Query',
        fields: () => {
          const fields = {};

          _.forOwn(this.models, (modelData) => {
            const defaultFieldName = fieldNameForModel(modelData.modelClass);
            const singleFieldName = modelData.opt.fieldName || defaultFieldName;
            const listFieldName = modelData.opt.listFieldName || (defaultFieldName + 's');

            fields[singleFieldName] = this._rootSingleField(modelData);
            fields[listFieldName] = this._rootListField(modelData);
          });

          return fields;
        }
      })
    });
  };

  _argsForModel(modelData) {
    const factories = defaultArgFactories(this.defaultArgNameMap, {typeCache: this.typeCache}).concat(this.argFactories);

    return factories.reduce((args, factory) => {
      return Object.assign(args, factory(modelData.fields, modelData.modelClass));
    }, {});
  }

  _rootSingleField(modelData) {
    return {
      type: this._typeForModel(modelData),
      args: modelData.args,
      resolve: this._resolverForModel(modelData, (query) => {
        query.first();
      })
    };
  }

  _rootListField(modelData) {
    return {
      type: new GraphQLList(this._typeForModel(modelData)),
      args: modelData.args,
      resolve: this._resolverForModel(modelData)
    };
  }

  _typeForModel(modelData) {
    const typeName = utils.typeNameForModel(modelData.modelClass);

    if (!this.typeCache[typeName]) {
      this.typeCache[typeName] = new GraphQLObjectType({
        name: typeName,
        fields: () => {
          return Object.assign({},
            this._attrFields(modelData),
            this._relationFields(modelData)
          )
        }
      });
    }

    return this.typeCache[typeName];
  }

  _attrFields(modelData) {
    return modelData.fields;
  }

  _relationFields(modelData) {
    const fields = {};

    _.forOwn(modelData.modelClass.getRelations(), (relation) => {
      const relationModel = this.models[relation.relatedModelClass.tableName];

      if (!relationModel) {
        // If the relation model has not been given for the builder using `model()` method
        // we don't handle the relations that have that class.
        return;
      }

      if (utils.isExcluded(relationModel.opt, relation.name)) {
        // If the property by the relation's name has been excluded, skip this relation.
        return;
      }

      fields[relation.name] = this._relationField(relationModel, relation);
    });

    return fields;
  }

  _relationField(modelData, relation) {
    if (relation instanceof objection.HasOneRelation
        || relation instanceof objection.BelongsToOneRelation
        || relation instanceof objection.HasOneThroughRelation) {
      return {
        type: this._typeForModel(modelData),
        args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS)
      };
    } else if (relation instanceof objection.HasManyRelation || relation instanceof objection.ManyToManyRelation) {
      return {
        type: new GraphQLList(this._typeForModel(modelData)),
        args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS)
      };
    } else {
      throw new Error(`relation type "${relation.constructor.name}" is not supported`);
    }
  }

  _resolverForModel(modelData, extraQuery) {
    return (ctx, ignore1, ignore2, data) => {
      ctx = ctx || {};

      const modelClass = modelData.modelClass;
      const ast = (data.fieldASTs || data.fieldNodes)[0];
      const eager = this._buildEager(ast, modelClass, data);
      const argFilter = this._filterForArgs(ast, modelClass, data.variableValues);
      const selectFilter = this._filterForSelects(ast, modelClass, data);
      const builder = modelClass.query(ctx.knex);

      if (ctx.onQuery) {
        ctx.onQuery(builder, ctx);
      }

      if (argFilter) {
        builder.modify(argFilter);
      }

      if (selectFilter) {
        builder.modify(selectFilter);
      }

      if (extraQuery) {
        builder.modify(extraQuery);
      }

      if (eager.expression) {
        builder.eager(eager.expression, eager.filters);
      }

      return builder.then(toJson);
    };
  }

  _buildEager(astNode, modelClass, astRoot) {
    const eagerExpr = this._buildEagerSegment(astNode, modelClass, astRoot);

    if (eagerExpr.expression.length) {
      eagerExpr.expression = `[${eagerExpr.expression}]`;
    }

    return eagerExpr;
  }

  _buildEagerSegment(astNode, modelClass, astRoot) {
    const filters = {};
    const relations = modelClass.getRelations();
    let expression = '';

    for (let i = 0, l = astNode.selectionSet.selections.length; i < l; ++i) {
      const selectionNode = astNode.selectionSet.selections[i];
      const relation = relations[selectionNode.name.value];

      if (relation) {
        expression = this._buildEagerRelationSegment(selectionNode, relation, expression, filters, astRoot);
      } else if (selectionNode.kind === KIND_FRAGMENT_SPREAD) {
        expression = this._buildEagerFragmentSegment(selectionNode, modelClass, expression, filters, astRoot);
      }
    }

    return {
      expression,
      filters
    };
  }

  _buildEagerRelationSegment(selectionNode, relation, expression, filters, astRoot) {
    let relExpr = selectionNode.name.value;

    const selectFilter = this._filterForSelects(selectionNode, relation.relatedModelClass, astRoot);
    const filterNames = [];

    if (selectFilter) {
      const filterName = `s${this.filterIndex++}`;

      filterNames.push(filterName);
      filters[filterName] = selectFilter;
    }

    if (selectionNode.arguments.length) {
      const argFilter = this._filterForArgs(selectionNode, relation.relatedModelClass, astRoot.variableValues);

      if (argFilter) {
        const filterName = `f${this.filterIndex++}`;

        filterNames.push(filterName);
        filters[filterName] = argFilter;
      }
    }

    if (filterNames.length) {
      relExpr += `(${filterNames.join(', ')})`;
    }

    const subExpr = this._buildEager(selectionNode, relation.relatedModelClass, astRoot);

    if (subExpr.expression.length) {
      relExpr += `.${subExpr.expression}`;
      Object.assign(filters, subExpr.filters);
    }

    if (expression.length) {
      expression += ', ';
    }

    return expression + relExpr;
  }

  _buildEagerFragmentSegment(selectionNode, modelClass, expression, filters, astRoot) {
    const fragmentSelection = astRoot.fragments[selectionNode.name.value];
    const fragmentExpr = this._buildEagerSegment(fragmentSelection, modelClass, astRoot);
    let fragmentExprString = '';

    if (fragmentExpr.expression.length) {
      fragmentExprString += fragmentExpr.expression;
      Object.assign(filters, fragmentExpr.filters);
    }

    if (expression.length) {
      expression += ', ';
    }

    return expression + fragmentExprString;
  }

  _filterForArgs(astNode, modelClass, variables) {
    const args = astNode.arguments;

    if (args.length === 0) {
      return null;
    }

    const modelData = this.models[modelClass.tableName];
    const argObjects = new Array(args.length);

    for (let i = 0, l = args.length; i < l; ++i) {
      const arg = args[i];
      const value = this._argValue(arg.value, variables);

      argObjects[i] = {
        name: arg.name.value,
        value
      };
    }

    return (builder) => {
      for (let i = 0, l = argObjects.length; i < l; ++i) {
        const arg = argObjects[i];
        modelData.args[arg.name].query(builder, arg.value);
      }
    };
  }

  _argValue(value, variables) {
    if (value.kind === KIND_VARIABLE) {
      return variables[value.name.value];
    } else if ('value' in value) {
      return value.value;
    } else if (Array.isArray(value.values)) {
      return value.values.map(value => this._argValue(value, variables));
    } else {
      throw new Error(`objection-graphql cannot handle argument value ${JSON.stringify(value)}`);
    }
  }

  _filterForSelects(astNode, modelClass, astRoot) {
    if (!this.enableSelectFiltering) {
      return null;
    }

    const relations = modelClass.getRelations();
    const selects = this._collectSelects(astNode, relations, astRoot.fragments, []);

    if (selects.length === 0) {
      return null;
    }

    return (builder) => {
      const jsonSchema = modelClass.jsonSchema;

      builder.select(selects.map(it => {
        const col = modelClass.propertyNameToColumnName(it);

        if (jsonSchema.properties[it]) {
          return `${builder.tableRefFor(modelClass)}.${col}`;
        } else {
          return col;
        }
      }));
    };
  }

  _collectSelects(astNode, relations, fragments, selects) {
    for (let i = 0, l = astNode.selectionSet.selections.length; i < l; ++i) {
      const selectionNode = astNode.selectionSet.selections[i];

      if (selectionNode.kind === KIND_FRAGMENT_SPREAD) {
        this._collectSelects(fragments[selectionNode.name.value], relations, fragments, selects);
      } else {
        const relation = relations[selectionNode.name.value];
        const isMetaField = GRAPHQL_META_FIELDS.indexOf(selectionNode.name.value) !== -1;

        if (!relation && !isMetaField) {
          selects.push(selectionNode.name.value);
        }
      }
    }

    return selects;
  }
}

function fieldNameForModel(modelClass) {
  return _.camelCase(utils.typeNameForModel(modelClass));
}

function toJson(result) {
  if (_.isArray(result)) {
    for (let i = 0, l = result.length; i < l; ++i) {
      result[i] = result[i].$toJson();
    }
  } else {
    result = result.$toJson();
  }

  return result;
}

module.exports = SchemaBuilder;
<commit_msg>Fixes #28<commit_after>'use strict';

var _ = require('lodash')
  , utils = require('./utils')
  , objection = require('objection')
  , graphqlRoot = require('graphql')
  , jsonSchemaUtils = require('./jsonSchema')
  , defaultArgFactories = require('./argFactories')
  , GraphQLObjectType = graphqlRoot.GraphQLObjectType
  , GraphQLSchema = graphqlRoot.GraphQLSchema
  , GraphQLList = graphqlRoot.GraphQLList;

// Default arguments that are excluded from the relation arguments.
const OMIT_FROM_RELATION_ARGS = [
  // We cannot use `range` in the relation arguments since the relations are fetched
  // for multiple objects at a time. Limiting the result set would limit the combined
  // result, and not the individual model's relation.
  "range"
];

const GRAPHQL_META_FIELDS = [
  "__typename"
];

// GraphQL AST node types.
const KIND_FRAGMENT_SPREAD = 'FragmentSpread';
const KIND_VARIABLE = 'Variable';

class SchemaBuilder {

  constructor() {
    this.models = {};
    this.typeCache = {};
    this.filterIndex = 1;
    this.argFactories = [];
    this.enableSelectFiltering = true;
    this.defaultArgNameMap = {
      "eq": 'Eq',
      "gt": 'Gt',
      "gte": 'Gte',
      "lt": 'Lt',
      "lte": 'Lte',
      "like": 'Like',
      "isNull": 'IsNull',
      "likeNoCase": 'LikeNoCase',
      "in": 'In',
      "notIn": 'NotIn',
      "orderBy": 'orderBy',
      "orderByDesc": 'orderByDesc',
      "range": "range"
    };
  }

  model(modelClass, opt) {
    opt = opt || {};

    if (!modelClass.jsonSchema) {
      throw new Error('modelClass must have a jsonSchema');
    }

    this.models[modelClass.tableName] = {
      modelClass: modelClass,
      fields: null,
      args: null,
      opt: opt
    };

    return this;
  };

  defaultArgNames(defaultArgNameMap) {
    this.defaultArgNameMap = Object.assign({}, this.defaultArgNameMap, defaultArgNameMap);
    return this;
  }

  argFactory(argFactory) {
    this.argFactories.push(argFactory);
    return this;
  }

  selectFiltering(enable) {
    this.enableSelectFiltering = !!enable;
    return this;
  }

  build() {
    _.forOwn(this.models, modelData => {
      modelData.fields = jsonSchemaUtils.jsonSchemaToGraphQLFields(modelData.modelClass.jsonSchema, {
        include: modelData.opt.include,
        exclude: modelData.opt.exclude,
        typeNamePrefix: utils.typeNameForModel(modelData.modelClass),
        typeCache: this.typeCache
      });

      modelData.args = this._argsForModel(modelData);
    });

    return new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'Query',
        fields: () => {
          const fields = {};

          _.forOwn(this.models, (modelData) => {
            const defaultFieldName = fieldNameForModel(modelData.modelClass);
            const singleFieldName = modelData.opt.fieldName || defaultFieldName;
            const listFieldName = modelData.opt.listFieldName || (defaultFieldName + 's');

            fields[singleFieldName] = this._rootSingleField(modelData);
            fields[listFieldName] = this._rootListField(modelData);
          });

          return fields;
        }
      })
    });
  };

  _argsForModel(modelData) {
    const factories = defaultArgFactories(this.defaultArgNameMap, {typeCache: this.typeCache}).concat(this.argFactories);

    return factories.reduce((args, factory) => {
      return Object.assign(args, factory(modelData.fields, modelData.modelClass));
    }, {});
  }

  _rootSingleField(modelData) {
    return {
      type: this._typeForModel(modelData),
      args: modelData.args,
      resolve: this._resolverForModel(modelData, (query) => {
        query.first();
      })
    };
  }

  _rootListField(modelData) {
    return {
      type: new GraphQLList(this._typeForModel(modelData)),
      args: modelData.args,
      resolve: this._resolverForModel(modelData)
    };
  }

  _typeForModel(modelData) {
    const typeName = utils.typeNameForModel(modelData.modelClass);

    if (!this.typeCache[typeName]) {
      this.typeCache[typeName] = new GraphQLObjectType({
        name: typeName,
        fields: () => {
          return Object.assign({},
            this._attrFields(modelData),
            this._relationFields(modelData)
          )
        }
      });
    }

    return this.typeCache[typeName];
  }

  _attrFields(modelData) {
    return modelData.fields;
  }

  _relationFields(modelData) {
    const fields = {};

    _.forOwn(modelData.modelClass.getRelations(), (relation) => {
      const relationModel = this.models[relation.relatedModelClass.tableName];

      if (!relationModel) {
        // If the relation model has not been given for the builder using `model()` method
        // we don't handle the relations that have that class.
        return;
      }

      if (utils.isExcluded(relationModel.opt, relation.name)) {
        // If the property by the relation's name has been excluded, skip this relation.
        return;
      }

      fields[relation.name] = this._relationField(relationModel, relation);
    });

    return fields;
  }

  _relationField(modelData, relation) {
    if (relation instanceof objection.HasOneRelation
        || relation instanceof objection.BelongsToOneRelation
        || relation instanceof objection.HasOneThroughRelation) {
      return {
        type: this._typeForModel(modelData),
        args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS)
      };
    } else if (relation instanceof objection.HasManyRelation || relation instanceof objection.ManyToManyRelation) {
      return {
        type: new GraphQLList(this._typeForModel(modelData)),
        args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS)
      };
    } else {
      throw new Error(`relation type "${relation.constructor.name}" is not supported`);
    }
  }

  _resolverForModel(modelData, extraQuery) {
    return (ctx, ignore1, ignore2, data) => {
      ctx = ctx || {};

      const modelClass = modelData.modelClass;
      const ast = (data.fieldASTs || data.fieldNodes)[0];
      const eager = this._buildEager(ast, modelClass, data);
      const argFilter = this._filterForArgs(ast, modelClass, data.variableValues);
      const selectFilter = this._filterForSelects(ast, modelClass, data);
      const builder = modelClass.query(ctx.knex);

      if (ctx.onQuery) {
        ctx.onQuery(builder, ctx);
      }

      if (argFilter) {
        builder.modify(argFilter);
      }

      if (selectFilter) {
        builder.modify(selectFilter);
      }

      if (extraQuery) {
        builder.modify(extraQuery);
      }

      if (eager.expression) {
        builder.eager(eager.expression, eager.filters);
      }

      return builder.then(toJson);
    };
  }

  _buildEager(astNode, modelClass, astRoot) {
    const eagerExpr = this._buildEagerSegment(astNode, modelClass, astRoot);

    if (eagerExpr.expression.length) {
      eagerExpr.expression = `[${eagerExpr.expression}]`;
    }

    return eagerExpr;
  }

  _buildEagerSegment(astNode, modelClass, astRoot) {
    const filters = {};
    const relations = modelClass.getRelations();
    let expression = '';

    for (let i = 0, l = astNode.selectionSet.selections.length; i < l; ++i) {
      const selectionNode = astNode.selectionSet.selections[i];
      const relation = relations[selectionNode.name.value];

      if (relation) {
        expression = this._buildEagerRelationSegment(selectionNode, relation, expression, filters, astRoot);
      } else if (selectionNode.kind === KIND_FRAGMENT_SPREAD) {
        expression = this._buildEagerFragmentSegment(selectionNode, modelClass, expression, filters, astRoot);
      }
    }

    return {
      expression,
      filters
    };
  }

  _buildEagerRelationSegment(selectionNode, relation, expression, filters, astRoot) {
    let relExpr = selectionNode.name.value;

    const selectFilter = this._filterForSelects(selectionNode, relation.relatedModelClass, astRoot);
    const filterNames = [];

    if (selectFilter) {
      const filterName = `s${this.filterIndex++}`;

      filterNames.push(filterName);
      filters[filterName] = selectFilter;
    }

    if (selectionNode.arguments.length) {
      const argFilter = this._filterForArgs(selectionNode, relation.relatedModelClass, astRoot.variableValues);

      if (argFilter) {
        const filterName = `f${this.filterIndex++}`;

        filterNames.push(filterName);
        filters[filterName] = argFilter;
      }
    }

    if (filterNames.length) {
      relExpr += `(${filterNames.join(', ')})`;
    }

    const subExpr = this._buildEager(selectionNode, relation.relatedModelClass, astRoot);

    if (subExpr.expression.length) {
      relExpr += `.${subExpr.expression}`;
      Object.assign(filters, subExpr.filters);
    }

    if (expression.length) {
      expression += ', ';
    }

    return expression + relExpr;
  }

  _buildEagerFragmentSegment(selectionNode, modelClass, expression, filters, astRoot) {
    const fragmentSelection = astRoot.fragments[selectionNode.name.value];
    const fragmentExpr = this._buildEagerSegment(fragmentSelection, modelClass, astRoot);
    let fragmentExprString = '';

    if (fragmentExpr.expression.length) {
      fragmentExprString += fragmentExpr.expression;
      Object.assign(filters, fragmentExpr.filters);
    }

    if (expression.length) {
      expression += ', ';
    }

    return expression + fragmentExprString;
  }

  _filterForArgs(astNode, modelClass, variables) {
    const args = astNode.arguments;

    if (args.length === 0) {
      return null;
    }

    const modelData = this.models[modelClass.tableName];
    const argObjects = new Array(args.length);

    for (let i = 0, l = args.length; i < l; ++i) {
      const arg = args[i];
      const value = this._argValue(arg.value, variables);

      argObjects[i] = {
        name: arg.name.value,
        value
      };
    }

    return (builder) => {
      for (let i = 0, l = argObjects.length; i < l; ++i) {
        const arg = argObjects[i];
        modelData.args[arg.name].query(builder, arg.value);
      }
    };
  }

  _argValue(value, variables) {
    if (value.kind === KIND_VARIABLE) {
      return variables[value.name.value];
    } else if ('value' in value) {
      return value.value;
    } else if (Array.isArray(value.values)) {
      return value.values.map(value => this._argValue(value, variables));
    } else {
      throw new Error(`objection-graphql cannot handle argument value ${JSON.stringify(value)}`);
    }
  }

  _filterForSelects(astNode, modelClass, astRoot) {
    if (!this.enableSelectFiltering) {
      return null;
    }

    const relations = modelClass.getRelations();
    const selects = this._collectSelects(astNode, relations, astRoot.fragments, []);

    if (selects.length === 0) {
      return null;
    }

    return (builder) => {
      const jsonSchema = modelClass.jsonSchema;

      builder.select(selects.map(it => {
        const col = modelClass.propertyNameToColumnName(it);

        if (jsonSchema.properties[it]) {
          return `${builder.tableRefFor(modelClass)}.${col}`;
        } else {
          return col;
        }
      }));
    };
  }

  _collectSelects(astNode, relations, fragments, selects) {
    for (let i = 0, l = astNode.selectionSet.selections.length; i < l; ++i) {
      const selectionNode = astNode.selectionSet.selections[i];

      if (selectionNode.kind === KIND_FRAGMENT_SPREAD) {
        this._collectSelects(fragments[selectionNode.name.value], relations, fragments, selects);
      } else {
        const relation = relations[selectionNode.name.value];
        const isMetaField = GRAPHQL_META_FIELDS.indexOf(selectionNode.name.value) !== -1;

        if (!relation && !isMetaField) {
          selects.push(selectionNode.name.value);
        }
      }
    }

    return selects;
  }
}

function fieldNameForModel(modelClass) {
  return _.camelCase(utils.typeNameForModel(modelClass));
}

function toJson(result) {
  if (_.isArray(result)) {
    for (let i = 0, l = result.length; i < l; ++i) {
      result[i] = result[i].$toJson();
    }
  } else {
    result = result && result.$toJson();
  }

  return result;
}

module.exports = SchemaBuilder;
<|endoftext|>


<commit_before>var React = require('react');
var {
    StyleSheet,
    Text,
    Image,
    View,
    TouchableHighlight,
    PropTypes,
    Dimensions
} = require('react-native');

var screen = Dimensions.get('window');
var tweenState = require('react-tween-state');

var styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    titleContainer: {
        flexDirection: 'row',
    },
    title: {
        //flex: 0,
        //backgroundColor: '#f3f3f3',
        alignItems: 'center',
        paddingHorizontal: 2,
        paddingVertical: 10,
    },
    spacer: {
        flex: 1,
    },
    barContainer: {
        height: 4,
        position: 'relative',
    },
    bar: {
        backgroundColor: 'blue',
        position: 'absolute',
        height: 4,
    }
});

var SegmentedView = React.createClass({

    mixins: [tweenState.Mixin],

    propTypes: {
        duration: PropTypes.number,
        onTransitionStart: PropTypes.func,
        onTransitionEnd: PropTypes.func,
        onPress: PropTypes.func,
        renderTitle: PropTypes.func,
        titles: PropTypes.array,
        index: PropTypes.number,
        barColor: PropTypes.string,
        barPosition: PropTypes.string,
        underlayColor: PropTypes.string,
        stretch: PropTypes.bool,
        selectedTitleStyle: PropTypes.object,
        titleStyle: PropTypes.object,

    },

    getDefaultProps() {
        return {
            duration: 200,
            onTransitionStart: ()=>{},
            onTransitionEnd: ()=>{},
            onPress: ()=>{},
            renderTitle: null,
            index: 0,
            barColor: '#44B7E1',
            barPosition:'top',
            underlayColor: '#CCCCCC',
            stretch: false,
            selectedTextStyle: null,
            textStyle: null,
        };
    },

    getInitialState() {
        return {
            barLeft: 0,
            barRight: screen.width,
        };
    },

    componentDidMount() {
        setTimeout(() => this.moveTo(this.props.index), 0);
    },

    componentWillReceiveProps(nextProps) {
        this.moveTo(nextProps.index);
    },

    measureHandler(ox, oy, width) {

        this.tweenState('barLeft', {
            easing: tweenState.easingTypes.easeInOutQuad,
            duration: 200,
            endValue: ox
        });

        this.tweenState('barRight', {
            easing: tweenState.easingTypes.easeInOutQuad,
            duration: 200,
            endValue: screen.width - ox - width,
            onEnd: this.props.onTransitionEnd
        });

        setTimeout(this.props.onTransitionStart, 0);
    },

    moveTo(index) {
        this.refs[index].measure(this.measureHandler);
    },

    _renderTitle(title, i) {
        return (
            <View style={styles.title}>
                <Text style={[this.props.titleStyle, i === this.props.index && this.props.selectedTitleStyle]}>{title}</Text>
            </View>
        );
    },

    renderTitle(title, i) {
        return (
            <View key={i} ref={i} style={{ flex: this.props.stretch ? 1 : 0 }}>
                <TouchableHighlight underlayColor={this.props.underlayColor} onPress={() => this.props.onPress(i)}>
                    {this.props.renderTitle ? this.props.renderTitle(title, i) : this._renderTitle(title, i)}
                </TouchableHighlight>
            </View>
        );
    },

    render() {
        var items = [];
        var titles = this.props.titles;

        if (!this.props.stretch) {
            items.push(<View key={`s`} style={styles.spacer} />);
        }

        for (var i = 0; i < titles.length; i++) {
            items.push(this.renderTitle(titles[i], i));
            if (!this.props.stretch) {
                items.push(<View key={`s${i}`} style={styles.spacer} />);
            }
        }
        var barContainer = (
          <View style={styles.barContainer}>
              <View ref="bar" style={[styles.bar, {
                  left: this.getTweeningValue('barLeft'),
                  right: this.getTweeningValue('barRight'),
                  backgroundColor: this.props.barColor
              }]} />
          </View>
        );
        return (
            <View {...this.props} style={[styles.container, this.props.style]}>
                {this.props.barPosition == 'top' && barContainer}
                <View style={styles.titleContainer}>
                    {items}
                </View>
                {this.props.barPosition == 'bottom' && barContainer}
            </View>
        );
    }
});

module.exports = SegmentedView;
<commit_msg>Additional fixes on references<commit_after>var React, { PropTypes } = require('react');
var {
    StyleSheet,
    Text,
    Image,
    View,
    TouchableHighlight,
    Dimensions
} = require('react-native');

var screen = Dimensions.get('window');
var tweenState = require('react-tween-state');

var styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    titleContainer: {
        flexDirection: 'row',
    },
    title: {
        //flex: 0,
        //backgroundColor: '#f3f3f3',
        alignItems: 'center',
        paddingHorizontal: 2,
        paddingVertical: 10,
    },
    spacer: {
        flex: 1,
    },
    barContainer: {
        height: 4,
        position: 'relative',
    },
    bar: {
        backgroundColor: 'blue',
        position: 'absolute',
        height: 4,
    }
});

var SegmentedView = React.createClass({

    mixins: [tweenState.Mixin],

    propTypes: {
        duration: PropTypes.number,
        onTransitionStart: PropTypes.func,
        onTransitionEnd: PropTypes.func,
        onPress: PropTypes.func,
        renderTitle: PropTypes.func,
        titles: PropTypes.array,
        index: PropTypes.number,
        barColor: PropTypes.string,
        barPosition: PropTypes.string,
        underlayColor: PropTypes.string,
        stretch: PropTypes.bool,
        selectedTitleStyle: PropTypes.object,
        titleStyle: PropTypes.object,

    },

    getDefaultProps() {
        return {
            duration: 200,
            onTransitionStart: ()=>{},
            onTransitionEnd: ()=>{},
            onPress: ()=>{},
            renderTitle: null,
            index: 0,
            barColor: '#44B7E1',
            barPosition:'top',
            underlayColor: '#CCCCCC',
            stretch: false,
            selectedTextStyle: null,
            textStyle: null,
        };
    },

    getInitialState() {
        return {
            barLeft: 0,
            barRight: screen.width,
        };
    },

    componentDidMount() {
        setTimeout(() => this.moveTo(this.props.index), 0);
    },

    componentWillReceiveProps(nextProps) {
        this.moveTo(nextProps.index);
    },

    measureHandler(ox, oy, width) {

        this.tweenState('barLeft', {
            easing: tweenState.easingTypes.easeInOutQuad,
            duration: 200,
            endValue: ox
        });

        this.tweenState('barRight', {
            easing: tweenState.easingTypes.easeInOutQuad,
            duration: 200,
            endValue: screen.width - ox - width,
            onEnd: this.props.onTransitionEnd
        });

        setTimeout(this.props.onTransitionStart, 0);
    },

    moveTo(index) {
        this.refs[index].measure(this.measureHandler);
    },

    _renderTitle(title, i) {
        return (
            <View style={styles.title}>
                <Text style={[this.props.titleStyle, i === this.props.index && this.props.selectedTitleStyle]}>{title}</Text>
            </View>
        );
    },

    renderTitle(title, i) {
        return (
            <View key={i} ref={i} style={{ flex: this.props.stretch ? 1 : 0 }}>
                <TouchableHighlight underlayColor={this.props.underlayColor} onPress={() => this.props.onPress(i)}>
                    {this.props.renderTitle ? this.props.renderTitle(title, i) : this._renderTitle(title, i)}
                </TouchableHighlight>
            </View>
        );
    },

    render() {
        var items = [];
        var titles = this.props.titles;

        if (!this.props.stretch) {
            items.push(<View key={`s`} style={styles.spacer} />);
        }

        for (var i = 0; i < titles.length; i++) {
            items.push(this.renderTitle(titles[i], i));
            if (!this.props.stretch) {
                items.push(<View key={`s${i}`} style={styles.spacer} />);
            }
        }
        var barContainer = (
          <View style={styles.barContainer}>
              <View ref="bar" style={[styles.bar, {
                  left: this.getTweeningValue('barLeft'),
                  right: this.getTweeningValue('barRight'),
                  backgroundColor: this.props.barColor
              }]} />
          </View>
        );
        return (
            <View {...this.props} style={[styles.container, this.props.style]}>
                {this.props.barPosition == 'top' && barContainer}
                <View style={styles.titleContainer}>
                    {items}
                </View>
                {this.props.barPosition == 'bottom' && barContainer}
            </View>
        );
    }
});

module.exports = SegmentedView;
<|endoftext|>


<commit_before>/**
 * # SocketManager
 *
 * Copyright(c) 2012 Stefano Balietti
 * MIT Licensed
 *
 * Game socket manager: registers of all open sockets and
 * connected clients
 *
 * ---
 *
 */

// Global scope

module.exports = SocketManager;

var ngc = require('nodegame-client');
var PlayerList = ngc.PlayerList;
var J = ngc.JSUS;

/**
 * ## SocketManager constructor
 *
 * Creates an instance of SocketManager
 *
 * @param {GameServer} server A GameServer instance
 */
function SocketManager(server) {

    /**
     * ### SocketManager.server
     *
     * Cyclic ref to the GameServer object containing this instance
     *
     */
    this.server = server;

    /**
     * ### SocketManager.sockets
     *
     *  List of available sockets
     */
    this.sockets = {};

    /**
     * ### SocketManager.clients
     *
     * Maps a client ID to a socket obj (e.g. Direct or SIO)
     *
     * @see SocketManager.sockets
     */
    this.clients = {};

    this.sysLogger = server.sysLogger;
    this.msgLogger = server.msgLogger;
}

// ## METHODS

/**
 * SocketManager.registerSocket
 *
 * Registers a new type of socket
 *
 * @param {string} name The name of the socket
 * @param {object} socket The connection socket
 *
 */
SocketManager.prototype.registerSocket = function(name, socket) {
    this.sockets[name] = socket;
};

/**
 * SocketManager.registerClient
 *
 * Matches a client to a socket
 *
 * @param client {string} The id of the client
 * @param socket {object} The socket through which the client is connected
 *
 */
SocketManager.prototype.registerClient = function(client, socket) {
    this.clients[client] = socket;
};

/**
 * ### SocketManager.send
 *
 * Low level primitive to send a game message
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {boolean} FALSE, if an error occurs
 */
SocketManager.prototype.send = function(gameMsg) {

    switch(gameMsg.to) {

    case 'ALL':
        return this.send2all(gameMsg);

    case 'ROOM':
        // TODO: do we need it?
        return;

    default:
        return this.send2client(gameMsg);

    }

};

/**
 * ### SocketManager.send
 *
 * Low level primitive to send a game message
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {boolean} FALSE, if an error occurs
 */
SocketManager.prototype.send2all = function(gameMsg) {
    var smoothExec, res, i;
    smoothExec = true;

    for (i in this.sockets) {
        if (this.sockets.hasOwnProperty(i)) {
            res = this.sockets[i].send(gameMsg, 'ALL');
            if (!res) {
                this.sysLogger.log('SocketManager.send2all: An error occurred in socket: ' + i, 'ERR');
                smoothExec = false;
            }
        }
    }
    return smoothExec;

};

/**
 * ### SocketManager.send2client
 *
 * Low level primitive to send a game message
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {boolean} FALSE, if an error occurs
 */
SocketManager.prototype.send2client = function(gameMsg) {
    var resolvedInfo, socket, receiverSid;
    var res, errMsg;

    resolvedInfo = this.resolveClientAndSocket(gameMsg);

    if (!resolvedInfo.success) {
        this.sysLogger.log('Msg not sent. ' + resolvedInfo.errMsg);
        return false;
    }

    socket = resolvedInfo.socket;
    receiverSid = resolvedInfo.sid;
    res = socket.send(gameMsg, receiverSid);

    if (!res) {
        errMsg = 'An error occurred while sending a message to ' +
            gameMsg.to + ' (cid: ' + resolvedInfo.cid + ', sid: ' + receiverSid + ')';
        this.sysLogger.log(errMsg, 'ERR');
    }
    return res;
};

// TODO check here what kind of data is input

function send2clients(gameMsg, cids) {
    var originalTo, res;
    res = true;
    originalTo = gameMsg.to;
    for (i = 0; i < cids.length; i++) {
        gameMsg.to = cids[i];
        res = res && this.send2client(gameMsg);
    }
    // The original _from_ field of the gameMsg must be re-set
    // to avoid that a wrong reuse of the msg in the callee
    gameMsg.to = originalTo;
    return res;
}

/**
 * ### SocketManager.send2room
 *
 * Sends a game message to all clients in the same room
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {GameRoom} room. The id or alias of the sender
 * @return {boolean} FALSE is an error occurs
 *
 * TODO: better to have it with the room name?
 */
SocketManager.prototype.send2room = function(gameMsg, room) {
    var res;
    res = send2clients(gameMsg, room.clients.id.getAllKeys());
    if (!res) this.sysLogger.log('SocketManager.send2room: an error occurred for room ' + room.name);
    return res;
};

/**
 * ### SocketManager.send2roomAdmins
 *
 * Sends a game message to all admins in the same room
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {GameRoom} room. The id or alias of the sender
 * @return {boolean} FALSE is an error occurs
 *
 * TODO: better to have it with the room name?
 */
SocketManager.prototype.send2roomAdmins = function(gameMsg, room) {
    var res;
    res = send2clients(gameMsg, room.admins.id.getAllKeys());
    if (!res) this.sysLogger.log('SocketManager.send2roomAdmins: an error has occurred for room ' + room.name);
    return res;
};

/**
 * ### SocketManager.send2roomAdmins
 *
 * Sends a game message to all admins in the same room
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {GameRoom} room. The id or alias of the sender
 * @return {boolean} FALSE is an error occurs
 *
 * TODO: better to have it with the room name?
 */
SocketManager.prototype.send2roomPlayers = function(gameMsg, room) {
    var res;
    res = send2clients(gameMsg, room.players.id.getAllKeys());
    if (!res) this.sysLogger.log('SocketManager.send2roomPlayers: an error has occurred for room ' + room.name);
    return res;
};

/**
 * ### SocketManager.send2group
 *
 * Sends a game message to a group of clients
 *
 * The methods works regardless of the socket or room the clients
 * belong to.
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {array|PlayerList} The recipients of the message
 * @return {boolean} TRUE on success
 */
SocketManager.prototype.send2group = function(gameMsg, group) {
    var res, i;
    res = true;

    if (!J.isArray(group)) {
        group = group.db;
    }
    for (i = 0; i < group.length; i++) {
        res = res && this.send2client(gameMsg, group[i].id);
    }

    if (!res) this.sysLogger.log('SocketManager.send2group: an error has occurred.');
    return res;
};

/**
 * ### SocketManager.resolveClientAndSocket
 *
 * Retrieves the socket, and sid of the receiver of a GameMsg
 *
 * Returns an object with the following properties:
 *
 *  - socket: the Socket object
 *  - sid: the id of the recipient within the socket
 *  - cid: the client id of the recipient within the channel
 *  - success: boolean flag, TRUE if resolving is successful
 *  - errMsg: set if an error occurs
 *
 * @param {GameMsg} gameMsg The game message to analyze
 * @return {object} An object containing the result of the
 *   resolving process
 */
SocketManager.prototype.resolveClientAndSocket = function(gameMsg) {
    var to, socket;
    var resolvedTo, registry;
    var receiverObj, receiverSid;

    registry = this.server.channel.registry;
    to = gameMsg.to;

    // TODO: move somewhere in the wiki
    // Game aliases are global associations with a client id that are valid throughout
    // the whole game, while room-aliases are valid only within a specific room.
    // When a client leaves that room the room alias is deleted.

    // Matches the 'to' (GameMsg.to) field with a client ID.
    // The 'to' field can be already a client ID, or a game-alias or a room-alias.
    // In case of a room-alias, uses 'from' (GameMsg.from) field to locate the room.
    // Can return null if the lookup fails.

    resolvedTo = registry.lookupClient(to,
                                       registry.getClientRoom(gameMsg.from));

    socket = this.clients[to];

    if (!socket) {
        return {
            success: false,
            errMsg: 'Unexisting recipient: ' + to + ' (resolved as: ' + resolvedTo + ')'
        };
    }

    receiverObj = registry.getClients()[resolvedTo];

    if (!receiverObj) {
         return {
            success: false,
            errMsg: 'Could not resolve the client id to a socket id'
         };
    }

    receiverSid = receiverObj.sid;

    if (!receiverSid) {
        return {
            success: false,
            errMsg: 'Player object has no socket id.'
        };
    }

    return {
        success: true,
        sid: receiverSid,
        cid: resolvedTo,
        socket: socket
    };
};

/**
 * ### SocketManager.broadcast
 *
 * Sends a game message to all clients in the same room except the sender
 *
 * The sender is taken from the 'from' field of the game message,
 * or it can be specified as a second parameter.
 *
 * TODO: This method at the moment sends message within ROOM only
 * TODO: create a new method that works across ROOMS
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {string} from Optional. The id or alias of the sender. Defaults,
 *    the value of the field gameMsg.from
 * @return {boolean} FALSE is an error occurs
 */
SocketManager.prototype.broadcast = function(gameMsg, from) {
    var client, originalTo, smoothExec;
    var registry = this.server.channel.registry;
    var resolvedFrom;
    var fromRoomName, fromRoom, toClients;

    smoothExec = true;
    originalTo = gameMsg.to;
    from = from || gameMsg.from;

    // Checks if from is an alias, and returns the id.
    // If it is not an alias, it still needs to be found
    // in the registry, otherwise it returns NULL.
    resolvedFrom = registry.lookupClient(from);
    fromRoomName = registry.getClientRoom(resolvedFrom);
    if (!fromRoomName) {
        throw new Error('SocketManager.broadcast: Sender (ID: ' + resolvedFrom + ') is not in any room');
    }
    else {
        fromRoom = this.server.channel.gameRooms[fromRoomName];
        if(!fromRoom) {
            throw new Error('SocketManager.broadcast: Sender (ID: ' + resolvedFrom + ') is not in any room');
        }
        else {
            toClients = fromRoom.clients.id.getAllKeyElements();
        }
    }

    for (client in toClients) {
        if (toClients.hasOwnProperty(client)) {
            if (client != resolvedFrom) {  // TODO: change back to '!==' once 'from' is string again
                gameMsg.to = client;
                smoothExec = smoothExec && this.send(gameMsg);
            }
        }
    }

    // The original _from_ field of the gameMsg must be re-set
    // to avoid that a wrong reuse of the msg in the callee
    gameMsg.to = originalTo;

    return smoothExec;
};

/**
 * ### SocketManager.broadcast2group
 *
 * Broadcasts a game message to a group of clients
 *
 * The methods works regardless of the socket or room the clients
 * belong to.
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {array|PlayerList} The recipients of the message
 * @param {string} from Optional. The id or alias of the sender. Defaults,
 *    the value of the field gameMsg.from
 * @return {boolean} TRUE on success
 */
SocketManager.prototype.broadcast2group = function(gameMsg, group, from) {
    var res, i;
    res = true;
    from = from || gameMsg.from;

    if (!J.isArray(group)) {
        group = group.db;
    }
    for (i = 0; i < group.length; i++) {
        // Do not send the msg to sender as well.
        if (group[i].id === from) continue;
        res = res && this.send2client(gameMsg, group[i].id);
    }

    if (!res) this.sysLogger.log('SocketManager.broadcast2group: an error has occurred.');
    return res;
};

/**
 * ### GameMsgManager.forward
 *
 * Low level primitive to forward messages
 *
 * Forward uses the send method of the partner server to
 * dispatch the message.
 *
 * @param {GameMsg} gameMsg The game message to forward
 */
SocketManager.prototype.forward = function(gameMsg) {

    // <!--
    // Create a copy of the msg and prepare the attributes
    // var gameMsg = new GameMsg(gameMsg);
    // gameMsg.from = this.server.name;
    // --!>

    gameMsg.forward = 1;

    if (gameMsg.to === 'SERVER' || !gameMsg.to) {
        gameMsg.to = 'ALL';
    }

    this.server.partner.socket.send(gameMsg);

    this.msgLogger.log('F', gameMsg);
    this.sysLogger.log('Msg ' + gameMsg.toSMS() + ' forwarded to ' + this.server.partner.name);
};

<commit_msg>Added 'to' parameter to SocketManager.send2client<commit_after>/**
 * # SocketManager
 *
 * Copyright(c) 2012 Stefano Balietti
 * MIT Licensed
 *
 * Game socket manager: registers of all open sockets and
 * connected clients
 *
 * ---
 *
 */

// Global scope

module.exports = SocketManager;

var ngc = require('nodegame-client');
var PlayerList = ngc.PlayerList;
var J = ngc.JSUS;

/**
 * ## SocketManager constructor
 *
 * Creates an instance of SocketManager
 *
 * @param {GameServer} server A GameServer instance
 */
function SocketManager(server) {

    /**
     * ### SocketManager.server
     *
     * Cyclic ref to the GameServer object containing this instance
     *
     */
    this.server = server;

    /**
     * ### SocketManager.sockets
     *
     *  List of available sockets
     */
    this.sockets = {};

    /**
     * ### SocketManager.clients
     *
     * Maps a client ID to a socket obj (e.g. Direct or SIO)
     *
     * @see SocketManager.sockets
     */
    this.clients = {};

    this.sysLogger = server.sysLogger;
    this.msgLogger = server.msgLogger;
}

// ## METHODS

/**
 * SocketManager.registerSocket
 *
 * Registers a new type of socket
 *
 * @param {string} name The name of the socket
 * @param {object} socket The connection socket
 *
 */
SocketManager.prototype.registerSocket = function(name, socket) {
    this.sockets[name] = socket;
};

/**
 * SocketManager.registerClient
 *
 * Matches a client to a socket
 *
 * @param client {string} The id of the client
 * @param socket {object} The socket through which the client is connected
 *
 */
SocketManager.prototype.registerClient = function(client, socket) {
    this.clients[client] = socket;
};

/**
 * ### SocketManager.send
 *
 * Low level primitive to send a game message
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {boolean} FALSE, if an error occurs
 */
SocketManager.prototype.send = function(gameMsg) {

    switch(gameMsg.to) {

    case 'ALL':
        return this.send2all(gameMsg);

    case 'ROOM':
        // TODO: do we need it?
        return;

    default:
        return this.send2client(gameMsg);

    }

};

/**
 * ### SocketManager.send
 *
 * Low level primitive to send a game message
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {boolean} FALSE, if an error occurs
 */
SocketManager.prototype.send2all = function(gameMsg) {
    var smoothExec, res, i;
    smoothExec = true;

    for (i in this.sockets) {
        if (this.sockets.hasOwnProperty(i)) {
            res = this.sockets[i].send(gameMsg, 'ALL');
            if (!res) {
                this.sysLogger.log('SocketManager.send2all: An error occurred in socket: ' + i, 'ERR');
                smoothExec = false;
            }
        }
    }
    return smoothExec;

};

/**
 * ### SocketManager.send2client
 *
 * Low level primitive to send a game message
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {boolean} FALSE, if an error occurs
 */
SocketManager.prototype.send2client = function(gameMsg, to) {
    var originalTo = gameMsg.to;
    var resolvedInfo, socket, receiverSid;
    var res, errMsg;

    gameMsg.to = to || originalTo;
    resolvedInfo = this.resolveClientAndSocket(gameMsg);

    if (!resolvedInfo.success) {
        gameMsg.to = originalTo;
        this.sysLogger.log('Msg not sent. ' + resolvedInfo.errMsg);
        return false;
    }

    socket = resolvedInfo.socket;
    receiverSid = resolvedInfo.sid;
    res = socket.send(gameMsg, receiverSid);

    if (!res) {
        errMsg = 'An error occurred while sending a message to ' +
            gameMsg.to + ' (cid: ' + resolvedInfo.cid + ', sid: ' + receiverSid + ')';
        this.sysLogger.log(errMsg, 'ERR');
    }
    gameMsg.to = originalTo;
    return res;
};

// TODO check here what kind of data is input

function send2clients(gameMsg, cids) {
    var res;
    res = true;
    for (i = 0; i < cids.length; i++) {
        res = res && this.send2client(gameMsg, cids[i]);
    }
    return res;
}

/**
 * ### SocketManager.send2room
 *
 * Sends a game message to all clients in the same room
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {GameRoom} room. The id or alias of the sender
 * @return {boolean} FALSE is an error occurs
 *
 * TODO: better to have it with the room name?
 */
SocketManager.prototype.send2room = function(gameMsg, room) {
    var res;
    res = send2clients(gameMsg, room.clients.id.getAllKeys());
    if (!res) this.sysLogger.log('SocketManager.send2room: an error occurred for room ' + room.name);
    return res;
};

/**
 * ### SocketManager.send2roomAdmins
 *
 * Sends a game message to all admins in the same room
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {GameRoom} room. The id or alias of the sender
 * @return {boolean} FALSE is an error occurs
 *
 * TODO: better to have it with the room name?
 */
SocketManager.prototype.send2roomAdmins = function(gameMsg, room) {
    var res;
    res = send2clients(gameMsg, room.admins.id.getAllKeys());
    if (!res) this.sysLogger.log('SocketManager.send2roomAdmins: an error has occurred for room ' + room.name);
    return res;
};

/**
 * ### SocketManager.send2roomAdmins
 *
 * Sends a game message to all admins in the same room
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {GameRoom} room. The id or alias of the sender
 * @return {boolean} FALSE is an error occurs
 *
 * TODO: better to have it with the room name?
 */
SocketManager.prototype.send2roomPlayers = function(gameMsg, room) {
    var res;
    res = send2clients(gameMsg, room.players.id.getAllKeys());
    if (!res) this.sysLogger.log('SocketManager.send2roomPlayers: an error has occurred for room ' + room.name);
    return res;
};

/**
 * ### SocketManager.send2group
 *
 * Sends a game message to a group of clients
 *
 * The methods works regardless of the socket or room the clients
 * belong to.
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {array|PlayerList} The recipients of the message
 * @return {boolean} TRUE on success
 */
SocketManager.prototype.send2group = function(gameMsg, group) {
    var res, i;
    res = true;

    if (!J.isArray(group)) {
        group = group.db;
    }
    for (i = 0; i < group.length; i++) {
        res = res && this.send2client(gameMsg, group[i].id);
    }

    if (!res) this.sysLogger.log('SocketManager.send2group: an error has occurred.');
    return res;
};

/**
 * ### SocketManager.resolveClientAndSocket
 *
 * Retrieves the socket, and sid of the receiver of a GameMsg
 *
 * Returns an object with the following properties:
 *
 *  - socket: the Socket object
 *  - sid: the id of the recipient within the socket
 *  - cid: the client id of the recipient within the channel
 *  - success: boolean flag, TRUE if resolving is successful
 *  - errMsg: set if an error occurs
 *
 * @param {GameMsg} gameMsg The game message to analyze
 * @return {object} An object containing the result of the
 *   resolving process
 */
SocketManager.prototype.resolveClientAndSocket = function(gameMsg) {
    var to, socket;
    var resolvedTo, registry;
    var receiverObj, receiverSid;

    registry = this.server.channel.registry;
    to = gameMsg.to;

    // TODO: move somewhere in the wiki
    // Game aliases are global associations with a client id that are valid throughout
    // the whole game, while room-aliases are valid only within a specific room.
    // When a client leaves that room the room alias is deleted.

    // Matches the 'to' (GameMsg.to) field with a client ID.
    // The 'to' field can be already a client ID, or a game-alias or a room-alias.
    // In case of a room-alias, uses 'from' (GameMsg.from) field to locate the room.
    // Can return null if the lookup fails.

    resolvedTo = registry.lookupClient(to,
                                       registry.getClientRoom(gameMsg.from));

    socket = this.clients[resolvedTo];

    if (!socket) {
        return {
            success: false,
            errMsg: 'Unexisting recipient: ' + to + ' (resolved as: ' + resolvedTo + ')'
        };
    }

    receiverObj = registry.getClients()[resolvedTo];

    if (!receiverObj) {
         return {
            success: false,
            errMsg: 'Could not resolve the client id to a socket id'
         };
    }

    receiverSid = receiverObj.sid;

    if (!receiverSid) {
        return {
            success: false,
            errMsg: 'Player object has no socket id.'
        };
    }

    return {
        success: true,
        sid: receiverSid,
        cid: resolvedTo,
        socket: socket
    };
};

/**
 * ### SocketManager.broadcast
 *
 * Sends a game message to all clients in the same room except the sender
 *
 * The sender is taken from the 'from' field of the game message,
 * or it can be specified as a second parameter.
 *
 * TODO: This method at the moment sends message within ROOM only
 * TODO: create a new method that works across ROOMS
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {string} from Optional. The id or alias of the sender. Defaults,
 *    the value of the field gameMsg.from
 * @return {boolean} FALSE is an error occurs
 */
SocketManager.prototype.broadcast = function(gameMsg, from) {
    var client, originalTo, smoothExec;
    var registry = this.server.channel.registry;
    var resolvedFrom;
    var fromRoomName, fromRoom, toClients;

    smoothExec = true;
    originalTo = gameMsg.to;
    from = from || gameMsg.from;

    // Checks if from is an alias, and returns the id.
    // If it is not an alias, it still needs to be found
    // in the registry, otherwise it returns NULL.
    resolvedFrom = registry.lookupClient(from);
    fromRoomName = registry.getClientRoom(resolvedFrom);
    if (!fromRoomName) {
        throw new Error('SocketManager.broadcast: Sender (ID: ' + resolvedFrom + ') is not in any room');
    }
    else {
        fromRoom = this.server.channel.gameRooms[fromRoomName];
        if(!fromRoom) {
            throw new Error('SocketManager.broadcast: Sender (ID: ' + resolvedFrom + ') is not in any room');
        }
        else {
            toClients = fromRoom.clients.id.getAllKeyElements();
        }
    }

    for (client in toClients) {
        if (toClients.hasOwnProperty(client)) {
            if (client != resolvedFrom) {  // TODO: change back to '!==' once 'from' is string again
                gameMsg.to = client;
                smoothExec = smoothExec && this.send(gameMsg);
            }
        }
    }

    // The original _from_ field of the gameMsg must be re-set
    // to avoid that a wrong reuse of the msg in the callee
    gameMsg.to = originalTo;

    return smoothExec;
};

/**
 * ### SocketManager.broadcast2group
 *
 * Broadcasts a game message to a group of clients
 *
 * The methods works regardless of the socket or room the clients
 * belong to.
 *
 * @param {GameMsg} gameMsg The game message to send
 * @param {array|PlayerList} The recipients of the message
 * @param {string} from Optional. The id or alias of the sender. Defaults,
 *    the value of the field gameMsg.from
 * @return {boolean} TRUE on success
 */
SocketManager.prototype.broadcast2group = function(gameMsg, group, from) {
    var res, i;
    res = true;
    from = from || gameMsg.from;

    if (!J.isArray(group)) {
        group = group.db;
    }
    for (i = 0; i < group.length; i++) {
        // Do not send the msg to sender as well.
        if (group[i].id === from) continue;
        res = res && this.send2client(gameMsg, group[i].id);
    }

    if (!res) this.sysLogger.log('SocketManager.broadcast2group: an error has occurred.');
    return res;
};

/**
 * ### GameMsgManager.forward
 *
 * Low level primitive to forward messages
 *
 * Forward uses the send method of the partner server to
 * dispatch the message.
 *
 * @param {GameMsg} gameMsg The game message to forward
 */
SocketManager.prototype.forward = function(gameMsg) {

    // <!--
    // Create a copy of the msg and prepare the attributes
    // var gameMsg = new GameMsg(gameMsg);
    // gameMsg.from = this.server.name;
    // --!>

    gameMsg.forward = 1;

    if (gameMsg.to === 'SERVER' || !gameMsg.to) {
        gameMsg.to = 'ALL';
    }

    this.server.partner.socket.send(gameMsg);

    this.msgLogger.log('F', gameMsg);
    this.sysLogger.log('Msg ' + gameMsg.toSMS() + ' forwarded to ' + this.server.partner.name);
};

<|endoftext|>


<commit_before>/*jslint node: true */
/*jshint laxbreak: true */
/*jshint laxcomma: true */
"use strict";

var d3 = require("d3");
var _ = require("underscore");
var ViewerHelper = require("./ViewerHelper");
var LegendDialog = require("./VariantLegendDialog");
var VariantFilterDialog = require("./VariantFilterDialog");
var Evidence = require('./Evidence');

//'G', 'A', 'V', 'L', 'I' aliphatic. 'S', 'T' hydroxyl. 'C', 'M' sulfur-containing. 'D', 'N', 'E', 'Q' acidic.
// 'R', 'K', 'H' basic. 'F', 'Y', 'W' aromatic. 'P' imino. '*' stop gained or lost.
var aaList = ['G', 'A', 'V', 'L', 'I'
    , 'S', 'T'
    , 'C', 'M'
    , 'D', 'N', 'E', 'Q'
    , 'R', 'K', 'H'
    , 'F', 'Y', 'W'
    , 'P'
    , '*'];

var drawMainSequence = function(variantViewer, bars) {
    var circle = bars.selectAll('circle')
        .data(function(d) {
            return [d];
        });

    circle
        .enter()
        .append('circle');

    circle.attr('cx', function(d) {
        return variantViewer.xScale(d.pos);
    })
        .attr('cy', function(d) {
            return variantViewer.yScale(d.normal);
        })
        .attr('r', 1)
        .attr('style','visibility:hidden')
        .attr('class', 'main-seq');

    circle.exit().remove();
};

var variantsFill = function(d, fv) {
    if((d.mutation === '*') || (d.begin > fv.maxPos)) {
        return LegendDialog.othersColor;
    } else if((d.sourceType === Evidence.variantSourceType.uniprot) ||
        (d.sourceType === Evidence.variantSourceType.mixed)) {
        if (Evidence.existAssociation(d.association)) {
            return LegendDialog.UPDiseaseColor;
        } else {
            return LegendDialog.UPNonDiseaseColor;
        }
    } else if (d.siftScore !== undefined) {
        return LegendDialog.getPredictionColor((d.siftScore + (1-d.polyphenScore))/2);
    } else {
        return LegendDialog.othersColor;
    }
};

var drawVariants = function(variantViewer, bars, frequency, fv, container, catTitle) {
    var variantCircle = bars.selectAll('circle')
        .data(function(d) {
            return d.variants;
        });

    var newCircles = variantCircle.enter().append('circle')
        .classed('up_pftv_variant', true)
        .attr('name', function(d) {
            return d.internalId;
        })
        .attr('fill', function(d) {
            return variantsFill(d, fv);
        })
        .attr('cy', function(d) {
            return variantViewer.yScale(d.mutation);
        })
    ;

    variantCircle
        .attr('r', function() {
            return VariantFilterDialog.displayFeature(d) ? frequency(0) : 0;
        })
        .attr('cx', function(d) {
            return variantViewer.xScale(Math.min(d.begin, fv.sequence.length));
        })
    ;

    ViewerHelper.addEventsClassAndTitle(catTitle, newCircles, fv, container);
    variantCircle.exit().remove();
};

var createDataSeries = function(variantViewer, svg, features, series) {
    var mainChart = svg.append('g')
        .attr('transform','translate(0,' + variantViewer.margin.top + ')');

    var chartArea = mainChart.append('g')
        .attr('clip-path', 'url(#plotAreaClip)');

    mainChart.append('clipPath')
        .attr('id', 'plotAreaClip')
        .append('rect')
        .attr({ width: (variantViewer.width -20) , height: variantViewer.height})
        .attr('transform','translate(10, -10)');

    var dataSeries = chartArea
        .datum(features)
        .call(series);

    var yAxis = d3.svg.axis()
        .scale(variantViewer.yScale)
        .tickSize(-variantViewer.width)
        .orient('left');

    var yAxis2 = d3.svg.axis()
        .scale(variantViewer.yScale)
        .orient('right');

    mainChart.append('g')
        .attr('transform','translate(12 ,0)')
        .attr('class','variation-y axis')
        .call(yAxis);

    mainChart.append('g')
        .attr('transform','translate(' + (variantViewer.width - 18) + ', 0)')
        .attr('class','variation-y axis')
        .call(yAxis2);

    return dataSeries;
};

var VariantViewer = function(catTitle, features, container, fv, variantHeight) {
    var variantViewer = this;
    variantViewer.height = variantHeight;
    variantViewer.width = fv.width;
    variantViewer.showManual = true;
    variantViewer.showAutomatic = true;
    variantViewer.xScale = fv.xScale;
    variantViewer.margin = {top:20, bottom:10};

    variantViewer.yScale = d3.scale.ordinal()
        .domain(aaList)
        .rangePoints([0, variantViewer.height - variantViewer.margin.top - variantViewer.margin.bottom]);

    var variationPlot = function() {
        var xScale = d3.scale.ordinal(),
            yScale = d3.scale.linear();

        var frequency = d3.scale.pow()
            .exponent(0.001)
            .domain([0, 1])
            .range([5, 10]);

        var variationPlot = function(selection) {
            var series, bars;

            selection.each(function(data) {
                // Generate chart
                series = d3.select(this);

                bars = series.selectAll('.up_pftv_var-series')
                    .data(data, function(d) {
                        return d.pos;
                    });

                bars.enter()
                    .append('g')
                    .classed('up_pftv_var-series', true);

                // drawMainSequence(variantViewer, bars);
                drawVariants(variantViewer, bars, frequency, fv, container, catTitle);

                bars.exit().remove();
            });
        };

        variationPlot.xScale = function(value) {
            if (!arguments.length) {
                return xScale;
            }
            xScale = value;
            return variationPlot;
        };

        variationPlot.yScale = function(value) {
            if (!arguments.length) {
                return yScale;
            }
            yScale = value;
            return variationPlot;
        };

        return variationPlot;
    };

    var svg = ViewerHelper.createSVG(container, variantViewer.width, variantViewer.height, fv, 'up_pftv_variants-svg');

    // Data series
    var series = variationPlot()
        .xScale(variantViewer.xScale)
        .yScale(variantViewer.yScale);

    var dataSeries = createDataSeries(variantViewer, svg, features, series);

    this.update = function() {
        dataSeries.call(series);
        if (fv.selectedFeature) {
            ViewerHelper.updateShadow(fv.selectedFeature, fv);
        }
    };

    return this;
};

module.exports = VariantViewer;<commit_msg>Add missing parameter d.<commit_after>/*jslint node: true */
/*jshint laxbreak: true */
/*jshint laxcomma: true */
"use strict";

var d3 = require("d3");
var _ = require("underscore");
var ViewerHelper = require("./ViewerHelper");
var LegendDialog = require("./VariantLegendDialog");
var VariantFilterDialog = require("./VariantFilterDialog");
var Evidence = require('./Evidence');

//'G', 'A', 'V', 'L', 'I' aliphatic. 'S', 'T' hydroxyl. 'C', 'M' sulfur-containing. 'D', 'N', 'E', 'Q' acidic.
// 'R', 'K', 'H' basic. 'F', 'Y', 'W' aromatic. 'P' imino. '*' stop gained or lost.
var aaList = ['G', 'A', 'V', 'L', 'I'
    , 'S', 'T'
    , 'C', 'M'
    , 'D', 'N', 'E', 'Q'
    , 'R', 'K', 'H'
    , 'F', 'Y', 'W'
    , 'P'
    , '*'];

var drawMainSequence = function(variantViewer, bars) {
    var circle = bars.selectAll('circle')
        .data(function(d) {
            return [d];
        });

    circle
        .enter()
        .append('circle');

    circle.attr('cx', function(d) {
        return variantViewer.xScale(d.pos);
    })
        .attr('cy', function(d) {
            return variantViewer.yScale(d.normal);
        })
        .attr('r', 1)
        .attr('style','visibility:hidden')
        .attr('class', 'main-seq');

    circle.exit().remove();
};

var variantsFill = function(d, fv) {
    if((d.mutation === '*') || (d.begin > fv.maxPos)) {
        return LegendDialog.othersColor;
    } else if((d.sourceType === Evidence.variantSourceType.uniprot) ||
        (d.sourceType === Evidence.variantSourceType.mixed)) {
        if (Evidence.existAssociation(d.association)) {
            return LegendDialog.UPDiseaseColor;
        } else {
            return LegendDialog.UPNonDiseaseColor;
        }
    } else if (d.siftScore !== undefined) {
        return LegendDialog.getPredictionColor((d.siftScore + (1-d.polyphenScore))/2);
    } else {
        return LegendDialog.othersColor;
    }
};

var drawVariants = function(variantViewer, bars, frequency, fv, container, catTitle) {
    var variantCircle = bars.selectAll('circle')
        .data(function(d) {
            return d.variants;
        });

    var newCircles = variantCircle.enter().append('circle')
        .classed('up_pftv_variant', true)
        .attr('name', function(d) {
            return d.internalId;
        })
        .attr('fill', function(d) {
            return variantsFill(d, fv);
        })
        .attr('cy', function(d) {
            return variantViewer.yScale(d.mutation);
        })
    ;

    variantCircle
        .attr('r', function(d) {
            return VariantFilterDialog.displayFeature(d) ? frequency(0) : 0;
        })
        .attr('cx', function(d) {
            return variantViewer.xScale(Math.min(d.begin, fv.sequence.length));
        })
    ;

    ViewerHelper.addEventsClassAndTitle(catTitle, newCircles, fv, container);
    variantCircle.exit().remove();
};

var createDataSeries = function(variantViewer, svg, features, series) {
    var mainChart = svg.append('g')
        .attr('transform','translate(0,' + variantViewer.margin.top + ')');

    var chartArea = mainChart.append('g')
        .attr('clip-path', 'url(#plotAreaClip)');

    mainChart.append('clipPath')
        .attr('id', 'plotAreaClip')
        .append('rect')
        .attr({ width: (variantViewer.width -20) , height: variantViewer.height})
        .attr('transform','translate(10, -10)');

    var dataSeries = chartArea
        .datum(features)
        .call(series);

    var yAxis = d3.svg.axis()
        .scale(variantViewer.yScale)
        .tickSize(-variantViewer.width)
        .orient('left');

    var yAxis2 = d3.svg.axis()
        .scale(variantViewer.yScale)
        .orient('right');

    mainChart.append('g')
        .attr('transform','translate(12 ,0)')
        .attr('class','variation-y axis')
        .call(yAxis);

    mainChart.append('g')
        .attr('transform','translate(' + (variantViewer.width - 18) + ', 0)')
        .attr('class','variation-y axis')
        .call(yAxis2);

    return dataSeries;
};

var VariantViewer = function(catTitle, features, container, fv, variantHeight) {
    var variantViewer = this;
    variantViewer.height = variantHeight;
    variantViewer.width = fv.width;
    variantViewer.showManual = true;
    variantViewer.showAutomatic = true;
    variantViewer.xScale = fv.xScale;
    variantViewer.margin = {top:20, bottom:10};

    variantViewer.yScale = d3.scale.ordinal()
        .domain(aaList)
        .rangePoints([0, variantViewer.height - variantViewer.margin.top - variantViewer.margin.bottom]);

    var variationPlot = function() {
        var xScale = d3.scale.ordinal(),
            yScale = d3.scale.linear();

        var frequency = d3.scale.pow()
            .exponent(0.001)
            .domain([0, 1])
            .range([5, 10]);

        var variationPlot = function(selection) {
            var series, bars;

            selection.each(function(data) {
                // Generate chart
                series = d3.select(this);

                bars = series.selectAll('.up_pftv_var-series')
                    .data(data, function(d) {
                        return d.pos;
                    });

                bars.enter()
                    .append('g')
                    .classed('up_pftv_var-series', true);

                // drawMainSequence(variantViewer, bars);
                drawVariants(variantViewer, bars, frequency, fv, container, catTitle);

                bars.exit().remove();
            });
        };

        variationPlot.xScale = function(value) {
            if (!arguments.length) {
                return xScale;
            }
            xScale = value;
            return variationPlot;
        };

        variationPlot.yScale = function(value) {
            if (!arguments.length) {
                return yScale;
            }
            yScale = value;
            return variationPlot;
        };

        return variationPlot;
    };

    var svg = ViewerHelper.createSVG(container, variantViewer.width, variantViewer.height, fv, 'up_pftv_variants-svg');

    // Data series
    var series = variationPlot()
        .xScale(variantViewer.xScale)
        .yScale(variantViewer.yScale);

    var dataSeries = createDataSeries(variantViewer, svg, features, series);

    this.update = function() {
        dataSeries.call(series);
        if (fv.selectedFeature) {
            ViewerHelper.updateShadow(fv.selectedFeature, fv);
        }
    };

    return this;
};

module.exports = VariantViewer;<|endoftext|>


<commit_before>/*
 * CSS Content Parser Adapter
 * @module   adapter/style
 * @author   genify(caijf@corp.netease.com)
 */
var util = require('util'),
    path = require('path'),
   _fs   = require('../util/file.js'),
   _path = require('../util/path.js');
// css parser
// file    - css file path
// content - css content
var Parser = module.exports =
    require('../util/klass.js').create();
var pro = Parser.extend(require('../util/event.js'));
/**
 * class initialization
 * @param  {Object} config - config parameters
 * @return {Void}
 */
pro.init = function(config){
    this._super(config);
    // init config
    config = config||{};
    this._file = config.file||'';
    this._source = config.content||'';
};
/**
 * parse file cotent
 * @param  {Object} config - parse config
 * @param  {String} config.webRoot  - web root path
 * @param  {String} config.resRoot  - resource root path
 * @return {Void}
 */
pro.parse = function(config){
    // parse css content
    var css = require('css'),
        ast = css.parse(
            this._source,{
                silent:!0,
                source:this._file
            }
        ),
        err = ast.stylesheet.parsingErrors;
    // check parse error
    if (!!err&&err.length>0){
        err = err[0];
        delete err.source;
        this.emit('warn',{
            data:[err],
            message:'illegal style content %j'
        });
        this._source = this._completeResource(
            this._source,config.webRoot,config.resRoot
        );
        return;
    }
    // complete resouce path
    this._checkResource(
        ast.stylesheet.rules||[],config
    );
    this._source = css.stringify(ast,{
        indent:0,
        compress:!0,
        sourcemap:!1,
        inputSourcemaps:!1
    });
};
/**
 * stringify file content
 * @param  {Object} config - parse config
 * @return {String} file content
 */
pro.stringify = function(){
    return this._source;
};
/**
 * check resource in rules
 * @private
 * @param  {Array} rules - rules for css content
 * @param  {Object} config - config object
 * @param  {String} config.webRoot - web root path
 * @param  {String} config.resRoot - resource root path
 * @return {Void}
 */
pro._checkResource = function(rules,config){
    var webRoot = config.webRoot,
        resRoot = config.resRoot;
    rules.forEach(function(rule){
        // for media query
        if (!!rule.rules){
            this._checkResource(
                rule.rules,config
            );
            return;
        }
        // for css selector
        (rule.declarations||[]).forEach(function(decl){
            if ((decl.value||'').indexOf('url(')>=0){
                decl.value = this._completeResource(
                    decl.value,webRoot,resRoot
                );
            }
        },this);
    },this);
};
/**
 * complete resource path
 * @private
 * @param  {String} str - css property string
 * @param  {String} webRoot - web root path
 * @param  {String} resRoot - resource root path
 * @return {String} string after complete resource path
 */
pro._completeResource = function(str,webRoot,resRoot){
    var pathRoot = path.dirname(this._file)+'/';
    return str.replace(
        /url\((['"])?(.*?)(['"])?\)/gi,function($1,$2,$3,$4){
            var ret = util.format(
                'url(%s)',$3
            );
            // $2 - ' or "
            // $3 - /path/to/image.png
            // $4 - ' or "
            // for remote or data url
            if ($3.indexOf(':')>0){
                // fix bug https://github.com/genify/toolkit2/issues/2
                ret = util.format(
                    'url(%s%s%s)',
                    $2||'',$3,$4||''
                );
                return ret;
            }
            // for local file
            var arr = $3.split('?');
            arr[0] = _path.absoluteAltRoot(
                arr[0],pathRoot,webRoot
            );
            // check file exist
            if (!_fs.exist(arr[0])){
                this.emit('warn',{
                    data:[arr[0],this._file],
                    message:'resource %s in file %s not exist'
                });
                return ret;
            }
            var uri = arr.join('?');
            // warn if resource not in res root
            if (!resRoot||arr[0].indexOf(resRoot)<0){
                this.emit('warn',{
                    data:[uri,this._file],
                    message:'%s is not in DIR_STATIC[%s]'
                });
                //return ret;
            }
            // check resource in web root
            if (!webRoot||arr[0].indexOf(webRoot)<0){
                return ret;
            }
            // use path placeholder
            this.emit('debug',{
                data:[uri,this._file],
                message:'find resource %s in file %s'
            });
            return util.format(
                'url(%s)',
                _path.wrapURI('rs',uri)
            );
        }.bind(this)
    );
};<commit_msg>split sprite images<commit_after>/*
 * CSS Content Parser Adapter
 * @module   adapter/style
 * @author   genify(caijf@corp.netease.com)
 */
var util = require('util'),
    path = require('path'),
   _io   = require('../util/io.js'),
   _fs   = require('../util/file.js'),
   _path = require('../util/path.js');
// css parser
// file    - css file path
// content - css content
var Parser = module.exports =
    require('../util/klass.js').create();
var pro = Parser.extend(require('../util/event.js'));
/**
 * class initialization
 * @param  {Object} config - config parameters
 * @return {Void}
 */
pro.init = function(config){
    this._super(config);
    // init config
    config = config||{};
    this._file = config.file||'';
    this._source = config.content||'';
};
/**
 * parse file cotent
 * @param  {Object} config - parse config
 * @param  {String} config.webRoot  - web root path
 * @param  {String} config.resRoot  - resource root path
 * @param  {String} config.sptRoot  - image sprite root path
 * @return {Void}
 */
pro.parse = function(config){
    // parse css content
    var css = require('css'),
        ast = css.parse(
            this._source,{
                silent:!0,
                source:this._file
            }
        ),
        err = ast.stylesheet.parsingErrors;
    // check parse error
    if (!!err&&err.length>0){
        err = err[0];
        delete err.source;
        this.emit('warn',{
            data:[err],
            message:'illegal style content %j'
        });
        this._source = this._completeResource(
            this._source,config.webRoot,
            config.resRoot,config.sptRoot
        );
        return;
    }
    // complete resouce path
    this._checkResource(
        ast.stylesheet.rules||[],config
    );
    this._source = css.stringify(ast,{
        indent:0,
        compress:!0,
        sourcemap:!1,
        inputSourcemaps:!1
    });
};
/**
 * stringify file content
 * @param  {Object} config - parse config
 * @return {String} file content
 */
pro.stringify = function(){
    return this._source;
};
/**
 * check resource in rules
 * @private
 * @param  {Array} rules - rules for css content
 * @param  {Object} config - config object
 * @param  {String} config.webRoot - web root path
 * @param  {String} config.resRoot - resource root path
 * @param  {String} config.sptRoot - sprite root path
 * @return {Void}
 */
pro._checkResource = function(rules,config){
    var webRoot = config.webRoot,
        resRoot = config.resRoot,
        sptRoot = config.sptRoot;
    rules.forEach(function(rule){
        // for media query
        if (!!rule.rules){
            this._checkResource(
                rule.rules,config
            );
            return;
        }
        // for css selector
        (rule.declarations||[]).forEach(function(decl){
            if ((decl.value||'').indexOf('url(')>=0){
                decl.value = this._completeResource(
                    decl.value,webRoot,resRoot,sptRoot
                );
            }
        },this);
    },this);
};
/**
 * complete resource path
 * @private
 * @param  {String} str - css property string
 * @param  {String} webRoot - web root path
 * @param  {String} resRoot - resource root path
 * @param  {String} sptRoot - sprite root path
 * @return {String} string after complete resource path
 */
pro._completeResource = function(str,webRoot,resRoot,sptRoot){
    var pathRoot = path.dirname(this._file)+'/';
    return str.replace(
        /url\((['"])?(.*?)(['"])?\)/gi,function($1,$2,$3,$4){
            var ret = util.format(
                'url(%s)',$3
            );
            // $2 - ' or "
            // $3 - /path/to/image.png
            // $4 - ' or "
            // for remote or data url
            if ($3.indexOf(':')>0){
                // fix bug https://github.com/genify/toolkit2/issues/2
                ret = util.format(
                    'url(%s%s%s)',
                    $2||'',$3,$4||''
                );
                return ret;
            }
            // for local file
            var arr = $3.split('?');
            arr[0] = _path.absoluteAltRoot(
                arr[0],pathRoot,webRoot
            );
            // check file exist
            if (!_fs.exist(arr[0])){
                this.emit('warn',{
                    data:[arr[0],this._file],
                    message:'resource %s in file %s not exist'
                });
                return ret;
            }
            // check sprite images
            if (!!sptRoot&&arr[0].indexOf(sptRoot)>=0){
                _io.cacheSpriteFile(arr[0]);
                return _path.wrapURI('sp',arr[0]);
            }
            var uri = arr.join('?');
            // warn if resource not in res root
            if (!resRoot||arr[0].indexOf(resRoot)<0){
                this.emit('warn',{
                    data:[uri,this._file],
                    message:'%s is not in DIR_STATIC[%s]'
                });
                //return ret;
            }
            // check resource in web root
            if (!webRoot||arr[0].indexOf(webRoot)<0){
                return ret;
            }
            // use path placeholder
            this.emit('debug',{
                data:[uri,this._file],
                message:'find resource %s in file %s'
            });
            return util.format(
                'url(%s)',
                _path.wrapURI('rs',uri)
            );
        }.bind(this)
    );
};<|endoftext|>


<commit_before>/**
* @file XML Validation helpers
*/
/*#ifndef(UMD)*/
"use strict";
/*global _gpfErrorDeclare*/ // Declare new gpf.Error names
/*exported _gpfXmlCheckValidElementName*/ // Check XML element name
/*exported _gpfXmlCheckValidAttributeName*/ // Check XML attribute name
/*exported _gpfXmlCheckValidNamespacePrefixName*/ // Check XML namespace prefix name
/*exported _gpfXmlCheckQualifiedElementName*/ // Check XML qualified element name
/*exported _gpfXmlCheckQualifiedAttributeName*/ // Check XML qualified attribute name
/*#endif*/

_gpfErrorDeclare("xml/check", {

    /**
    * ### Summary
    *
    * Invalid XML element name
    *
    * ### Description
    *
    * Invalid XML element name
    */
    invalidXmlElementName: "Invalid XML element name",

    /**
    * ### Summary
    *
    * Invalid XML attribute name
    *
    * ### Description
    *
    * Invalid XML attribute name
    */
    invalidXmlAttributeName: "Invalid XML attribute name",

    /**
    * ### Summary
    *
    * Invalid XML namespace prefix
    *
    * ### Description
    *
    * Invalid XML namespace prefix
    */
    invalidXmlNamespacePrefix: "Invalid XML namespace prefix",

    /**
    * ### Summary
    *
    * Invalid use of XML namespace prefix xmlns
    *
    * ### Description
    *
    * Invalid use of XML namespace prefix xmlns: startPrefixMapping should be used instead
    */
    invalidXmlUseOfPrefixXmlns: "Invalid use of XML namespace prefix xmlns",

    /**
    * ### Summary
    *
    * Invalid use of XML namespace prefix xml
    *
    * ### Description
    *
    * Invalid use of XML namespace prefix xml: only xml:space="preserve" is allowed
    */
    invalidXmlUseOfPrefixXml: "Invalid use of XML namespace prefix xml",

    /**
    * ### Summary
    *
    * Unknown XML namespace prefix
    *
    * ### Description
    *
    * This error is triggered when an element or an attribute is prefixed with an unknown namespace prefix
    */
    unknownXmlNamespacePrefix: "Unknown XML namespace prefix"

});

function _gpfXmlCheckBuildSimple (regexp, exception) {
    return function (name) {
        if (!name.match(regexp)) {
            gpf.Error["invalidXml" + exception]();
        }
    };
}

var
    _gpfXmlCheckNameRegExp = new RegExp("^[a-zA-Z_][a-zA-Z0-9_\\-\\.]*$"),
    _gpfXmlNamespacePrefixRegExp = new RegExp("^(|[a-z_][a-zA-Z0-9_]*)$"),

    /**
     * Check XML element name
     *
     * @param {String} name Element name to check
     * @throws {gpf.Error.InvalidXmlElementName}
     */
    _gpfXmlCheckValidElementName = _gpfXmlCheckBuildSimple(_gpfXmlCheckNameRegExp, "ElementName"),

    /**
     * Check XML attribute name
     *
     * @param {String} name Attribute name to check
     * @throws {gpf.Error.InvalidXmlAttributeName}
     */
    _gpfXmlCheckValidAttributeName = _gpfXmlCheckBuildSimple(_gpfXmlCheckNameRegExp, "AttributeName"),

    /**
    * Check XML namespace prefix name
    *
    * @param {String} name Namespace prefix name to check
    * @throws {gpf.Error.InvalidXmlNamespacePrefixName}
    */
    _gpfXmlCheckValidNamespacePrefixName = _gpfXmlCheckBuildSimple(_gpfXmlNamespacePrefixRegExp, "NamespacePrefix");

function _gpfXmlCheckNoXmlns (prefix) {
    if ("xmlns" === prefix) {
        gpf.Error.invalidXmlUseOfPrefixXmlns();
    }
}

function _gpfXmlCheckQualifiedNameAndPrefix (name, prefix) {
    _gpfXmlCheckValidElementName(name);
    _gpfXmlCheckValidNamespacePrefixName(prefix);
    _gpfXmlCheckNoXmlns(prefix);
}

function _gpfXmlCheckIfKnownPrefix (prefix, knownPrefixes) {
    if (knownPrefixes.indexOf(prefix) === -1) {
        gpf.Error.unknownXmlNamespacePrefix();
    }
}

function _gpfXmlCheckQualifiedElementNameAndPrefix (name, prefix, knownPrefixes) {
    _gpfXmlCheckQualifiedNameAndPrefix(name, prefix);
    if ("xml" === prefix) {
        gpf.Error.invalidXmlUseOfPrefixXml();
    }
    _gpfXmlCheckIfKnownPrefix(prefix, knownPrefixes);
}

function _gpfXmlCheckGetQualified (noPrefixCheck, prefixCheck) {
    return function (qName, knownPrefixes) {
        var sep = qName.indexOf(":");
        if (-1 === sep) {
            noPrefixCheck(qName);
        } else {
            prefixCheck(qName.substr(0, sep), qName.substr(sep + 1), knownPrefixes);
        }
    };
}

/**
 * Check XML qualified element name
 *
 * @param {String} qName Element qualified name to check
 * @param {String[]} knownPrefixes Known namespaces prefixes
 *
 * @throws {gpf.Error.InvalidXmlElementName}
 * @throws {gpf.Error.invalidXmlNamespacePrefix}
 */
var _gpfXmlCheckQualifiedElementName = _gpfXmlCheckGetQualified(_gpfXmlCheckValidElementName,
    _gpfXmlCheckQualifiedElementNameAndPrefix);

function _gpfXmlCheckOnlyXmlSpace (name) {
    if ("space" !== name) {
        gpf.Error.invalidXmlUseOfPrefixXml();
    }
}

function _gpfXmlCheckQualifiedAttributeNameAndPrefix (name, prefix, knownPrefixes) {
    _gpfXmlCheckQualifiedNameAndPrefix(name, prefix);
    if ("xml" === prefix) {
        _gpfXmlCheckOnlyXmlSpace(name);
    }
    _gpfXmlCheckIfKnownPrefix(prefix, knownPrefixes);
}

/**
 * Check XML qualified attribute name
 *
 * @param {String} qName Attribute qualified name to check
 * @param {String[]} knownPrefixes Known namespaces prefixes
 *
 * @throws {gpf.Error.InvalidXmlElementName}
 */
var _gpfXmlCheckQualifiedAttributeName = _gpfXmlCheckGetQualified(_gpfXmlCheckValidAttributeName,
    _gpfXmlCheckQualifiedAttributeNameAndPrefix);
<commit_msg>Finalizing check (#266)<commit_after>/**
* @file XML Validation helpers
* @since 0.2.7
*/
/*#ifndef(UMD)*/
"use strict";
/*global _gpfErrorDeclare*/ // Declare new gpf.Error names
/*exported _gpfXmlCheckDefinableNamespacePrefixName*/ // Check if the given XML namespace prefix name can be defined
/*exported _gpfXmlCheckQualifiedAttributeName*/ // Check XML qualified attribute name
/*exported _gpfXmlCheckQualifiedElementName*/ // Check XML qualified element name
/*exported _gpfXmlCheckValidAttributeName*/ // Check XML attribute name
/*exported _gpfXmlCheckValidElementName*/ // Check XML element name
/*exported _gpfXmlCheckValidNamespacePrefixName*/ // Check XML namespace prefix name
/*#endif*/

_gpfErrorDeclare("xml/check", {

    /**
    * ### Summary
    *
    * Invalid XML element name
    *
    * ### Description
    *
    * Invalid XML element name
    * @since 0.2.7
    */
    invalidXmlElementName: "Invalid XML element name",

    /**
    * ### Summary
    *
    * Invalid XML attribute name
    *
    * ### Description
    *
    * Invalid XML attribute name
    * @since 0.2.7
    */
    invalidXmlAttributeName: "Invalid XML attribute name",

    /**
    * ### Summary
    *
    * Invalid XML namespace prefix
    *
    * ### Description
    *
    * Invalid XML namespace prefix
    * @since 0.2.7
    */
    invalidXmlNamespacePrefix: "Invalid XML namespace prefix",

    /**
    * ### Summary
    *
    * Invalid use of XML namespace prefix xmlns
    *
    * ### Description
    *
    * Invalid use of XML namespace prefix xmlns: startPrefixMapping should be used instead
    * @since 0.2.7
    */
    invalidXmlUseOfPrefixXmlns: "Invalid use of XML namespace prefix xmlns",

    /**
    * ### Summary
    *
    * Invalid use of XML namespace prefix xml
    *
    * ### Description
    *
    * Invalid use of XML namespace prefix xml: only xml:space="preserve" is allowed
    * @since 0.2.7
    */
    invalidXmlUseOfPrefixXml: "Invalid use of XML namespace prefix xml",

    /**
    * ### Summary
    *
    * Unknown XML namespace prefix
    *
    * ### Description
    *
    * This error is triggered when an element or an attribute is prefixed with an unknown namespace prefix
    * @since 0.2.7
    */
    unknownXmlNamespacePrefix: "Unknown XML namespace prefix"

});

function _gpfXmlCheckBuildSimple (regexp, exception) {
    return function (name) {
        if (!name.match(regexp)) {
            gpf.Error["invalidXml" + exception]();
        }
    };
}

var
    _gpfXmlCheckNameRegExp = new RegExp("^[a-zA-Z_][a-zA-Z0-9_\\-\\.]*$"),
    _gpfXmlNamespacePrefixRegExp = new RegExp("^(|[a-z_][a-zA-Z0-9_]*)$"),

    /**
     * Check XML element name
     *
     * @param {String} name Element name to check
     * @throws {gpf.Error.InvalidXmlElementName}
     * @since 0.2.7
     */
    _gpfXmlCheckValidElementName = _gpfXmlCheckBuildSimple(_gpfXmlCheckNameRegExp, "ElementName"),

    /**
     * Check XML attribute name
     *
     * @param {String} name Attribute name to check
     * @throws {gpf.Error.InvalidXmlAttributeName}
     * @since 0.2.7
     */
    _gpfXmlCheckValidAttributeName = _gpfXmlCheckBuildSimple(_gpfXmlCheckNameRegExp, "AttributeName"),

    /**
    * Check XML namespace prefix name
    *
    * @param {String} name Namespace prefix name to check
    * @throws {gpf.Error.InvalidXmlNamespacePrefix}
    * @since 0.2.7
    */
    _gpfXmlCheckValidNamespacePrefixName = _gpfXmlCheckBuildSimple(_gpfXmlNamespacePrefixRegExp, "NamespacePrefix");

function _gpfXmlCheckNoXmlns (prefix) {
    if ("xmlns" === prefix) {
        gpf.Error.invalidXmlUseOfPrefixXmlns();
    }
}

function _gpfXmlCheckQualifiedNameAndPrefix (name, prefix) {
    _gpfXmlCheckValidElementName(name);
    _gpfXmlCheckValidNamespacePrefixName(prefix);
    _gpfXmlCheckNoXmlns(prefix);
}

function _gpfXmlCheckIfKnownPrefix (prefix, knownPrefixes) {
    if (knownPrefixes.indexOf(prefix) === -1) {
        gpf.Error.unknownXmlNamespacePrefix();
    }
}

function _gpfXmlCheckQualifiedElementNameAndPrefix (name, prefix, knownPrefixes) {
    _gpfXmlCheckQualifiedNameAndPrefix(name, prefix);
    if ("xml" === prefix) {
        gpf.Error.invalidXmlUseOfPrefixXml();
    } else {
        _gpfXmlCheckIfKnownPrefix(prefix, knownPrefixes);
    }
}

function _gpfXmlCheckGetQualified (noPrefixCheck, nameAndPrefixCheck) {
    return function (qName, knownPrefixes) {
        var sep = qName.indexOf(":");
        if (-1 === sep) {
            noPrefixCheck(qName);
        } else {
            nameAndPrefixCheck(qName.substr(sep + 1), qName.substr(0, sep), knownPrefixes);
        }
    };
}

/**
 * Check XML qualified element name
 *
 * @param {String} qName Element qualified name to check
 * @param {String[]} knownPrefixes Known namespaces prefixes
 *
 * @throws {gpf.Error.InvalidXmlElementName}
 * @throws {gpf.Error.invalidXmlNamespacePrefix}
 * @since 0.2.7
 */
var _gpfXmlCheckQualifiedElementName = _gpfXmlCheckGetQualified(_gpfXmlCheckValidElementName,
    _gpfXmlCheckQualifiedElementNameAndPrefix);

function _gpfXmlCheckOnlyXmlSpace (name) {
    if ("space" !== name) {
        gpf.Error.invalidXmlUseOfPrefixXml();
    }
}

function _gpfXmlCheckQualifiedAttributeNameAndPrefix (name, prefix, knownPrefixes) {
    _gpfXmlCheckQualifiedNameAndPrefix(name, prefix);
    if ("xml" === prefix) {
        _gpfXmlCheckOnlyXmlSpace(name);
    } else {
        _gpfXmlCheckIfKnownPrefix(prefix, knownPrefixes);
    }
}

/**
 * Check XML qualified attribute name
 *
 * @param {String} qName Attribute qualified name to check
 * @param {String[]} knownPrefixes Known namespaces prefixes
 *
 * @throws {gpf.Error.InvalidXmlElementName}
 * @since 0.2.7
 */
var _gpfXmlCheckQualifiedAttributeName = _gpfXmlCheckGetQualified(_gpfXmlCheckValidAttributeName,
    _gpfXmlCheckQualifiedAttributeNameAndPrefix);

/**
 * Check if the given XML namespace prefix name can be defined
 *
 * @param {String} name Namespace prefix name to check
 * @throws {gpf.Error.InvalidXmlNamespacePrefix}
 * @throws {gpf.Error.InvalidXmlUseOfPrefixXmlns}
 * @throws {gpf.Error.InvalidXmlUseOfPrefixXml}
 * @since 0.2.7
 */
function _gpfXmlCheckDefinableNamespacePrefixName (name) {
    _gpfXmlCheckValidNamespacePrefixName(name);
    _gpfXmlCheckNoXmlns(name);
    if ("xml" === name) {
        gpf.Error.invalidXmlUseOfPrefixXml();
    }
}
<|endoftext|>