<commit_before>const COMPARISON_OPERATORS = ['eq', 'ne', 'gt', 'ge', 'lt', 'le'];
const LOGICAL_OPERATORS = ['and', 'or', 'not'];
const COLLECTION_OPERATORS = ['any', 'all'];
const BOOLEAN_FUNCTIONS = ['startswith', 'endswith', 'contains'];
const SUPPORTED_EXPAND_PROPERTIES = [
  'expand',
  'select',
  'top',
  'orderby',
  'filter'
];

const FUNCTION_REGEX = /\((.*)\)/;

export default function({
  select,
  filter,
  search,
  groupBy,
  transform,
  orderBy,
  top,
  skip,
  key,
  count,
  expand,
  action,
  func,
  format
} = {}) {
  let path = '';
  const params = {};

  if (select) {
    params.$select = select;
  }

  if (filter || count instanceof Object) {
    const builtFilter = buildFilter(count instanceof Object ? count : filter);
    if (builtFilter !== undefined) {
      params.$filter = builtFilter;
    }
  }

  if (search) {
    params.$search = search;
  }

  if (transform) {
    const builtTransforms = buildTransforms(transform);
    if (builtTransforms !== undefined) {
      params.$apply = builtTransforms;
    }
  }

  if (top) {
    params.$top = top;
  }

  if (skip) {
    params.$skip = skip;
  }

  if (key) {
    if (typeof key === 'object') {
      const keys = Object.keys(key)
        .map(k => `${k}=${key[k]}`)
        .join(',');
      path += `(${keys})`;
    } else {
      path += `(${key})`;
    }
  }

  if (count) {
    if (typeof count === 'boolean') {
      params.$count = true;
    } else {
      path += '/$count';
    }
  }

  if (action) {
    path += `/${action}`;
  }

  if (func) {
    if (typeof func === 'string') {
      path += `/${func}`;
    } else if (typeof func === 'object') {
      const [funcName] = Object.keys(func);
      const funcArgs = Object.keys(func[funcName]).reduce(
        (acc, item) => {
          const value = func[funcName][item];
          if (Array.isArray(value) && typeof value[0] === 'object') {
            acc.params.push(`${item}=@${item}`);
            acc.aliases.push(`@${item}=${escape(JSON.stringify(value))}`);
          } else {
            acc.params.push(`${item}=${handleValue(value)}`);
          }
          return acc;
        },
        { params: [], aliases: [] }
      );

      path += `/${funcName}`;
      if (funcArgs.params.length) {
        path += `(${funcArgs.params.join(',')})`;
      }
      if (funcArgs.aliases.length) {
        path += `?${funcArgs.aliases.join(',')}`;
      }
    }
  }

  if (expand) {
    params.$expand = buildExpand(expand);
  }

  if (orderBy) {
    params.$orderby = buildOrderBy(orderBy);
  }

  if(format) {
    params.$format = format;
  }

  return buildUrl(path, params);
}

function buildFilter(filters = {}, propPrefix = '') {
  if (filters == null) {
    // ignore `null` and `undefined` filters (useful for conditionally applied filters)
    return;
  } else if (typeof filters === 'string') {
    // Use raw filter string
    return filters;
  } else if (Array.isArray(filters)) {
    const builtFilters = filters
      .map(f => buildFilter(f, propPrefix))
      .filter(f => f !== undefined);
    if (builtFilters.length) {
      return `${builtFilters.map(f => `(${f})`).join(` and `)}`;
    }
  } else if (typeof filters === 'object') {
    const filtersArray = Object.keys(filters).reduce((result, filterKey) => {
      const value = filters[filterKey];
      const propName = propPrefix
        ? FUNCTION_REGEX.test(filterKey)
          ? filterKey.replace(FUNCTION_REGEX, `(${propPrefix}/$1)`)
          : `${propPrefix}/${filterKey}`
        : filterKey;

      if (
        ['number', 'string', 'boolean'].indexOf(typeof value) !== -1 ||
        value instanceof Date ||
        value === null
      ) {
        // Simple key/value handled as equals operator
        result.push(`${propName} eq ${handleValue(value)}`);
      } else if (Array.isArray(value)) {
        const op = filterKey;
        const builtFilters = value
          .map(v => buildFilter(v, propPrefix))
          .filter(f => f !== undefined)
          .map(f => (LOGICAL_OPERATORS.indexOf(op) !== -1 ? `(${f})` : f));
        if (builtFilters.length) {
          if (LOGICAL_OPERATORS.indexOf(op) !== -1) {
            result.push(parseNot(op, builtFilters, true));
          } else {
            result.push(builtFilters.join(` ${op} `));
          }
        }
      } else if (LOGICAL_OPERATORS.indexOf(propName) !== -1) {
        const op = propName;
        const builtFilters = Object.keys(value).map(valueKey =>
          buildFilter({ [valueKey]: value[valueKey] })
        );
        if (builtFilters.length) {
          result.push(parseNot(op, builtFilters, false));
        }
      } else if (value instanceof Object) {
        if ('type' in value) {
          result.push(`${propName} eq ${handleValue(value)}`);
        } else {
          const operators = Object.keys(value);
          operators.forEach(op => {
            if (COMPARISON_OPERATORS.indexOf(op) !== -1) {
              result.push(`${propName} ${op} ${handleValue(value[op])}`);
            } else if (LOGICAL_OPERATORS.indexOf(op) !== -1) {
              if (Array.isArray(value[op])) {
                result.push(
                  value[op]
                    .map(v => '(' + buildFilter(v, propName) + ')')
                    .join(` ${op} `)
                );
              } else {
                result.push('(' + buildFilter(value[op], propName) + ')');
              }
            } else if (COLLECTION_OPERATORS.indexOf(op) !== -1) {
              const lambaParameter = filterKey[0].toLowerCase();
              const filter = buildFilter(value[op], lambaParameter);

              if (filter !== undefined) {
                // Do not apply collection filter if undefined (ex. ignore `Foo: { any: {} }`)
                result.push(`${propName}/${op}(${lambaParameter}:${filter})`);
              }
            } else if (op === 'in') {
              const resultingValues = Array.isArray(value[op])
                ? // Convert `{ Prop: { in: [1,2,3] } }` to `(Prop eq 1 or Prop eq 2 or Prop eq 3)`
                  value[op]
                : // Convert `{ Prop: { in: [{type: type, value: 1},{type: type, value: 2},{type: type, value: 3}] } }`
                  // to `(Prop eq 1 or Prop eq 2 or Prop eq 3)`
                  value[op].value.map(typedValue => ({
                    type: value[op].type,
                    value: typedValue
                  }));

              result.push(
                '(' +
                  resultingValues
                    .map(v => `${propName} eq ${handleValue(v)}`)
                    .join(' or ') +
                  ')'
              );
            } else if (BOOLEAN_FUNCTIONS.indexOf(op) !== -1) {
              // Simple boolean functions (startswith, endswith, contains)
              result.push(`${op}(${propName},${handleValue(value[op])})`);
            } else {
              // Nested property
              result.push(buildFilter(value, propName));
            }
          });
        }
      } else if (value === undefined) {
        // Ignore/omit filter if value is `undefined`
      } else {
        throw new Error(`Unexpected value type: ${value}`);
      }

      return result;
    }, []);

    return filtersArray.join(' and ') || undefined;
  } else {
    throw new Error(`Unexpected filters type: ${filters}`);
  }
}

function escapeIllegalChars(string) {
  string = string.replace(/%/g, '%25');
  string = string.replace(/\+/g, '%2B');
  string = string.replace(/\//g, '%2F');
  string = string.replace(/\?/g, '%3F');
  string = string.replace(/#/g, '%23');
  string = string.replace(/&/g, '%26');
  string = string.replace(/'/g, "''");
  return string;
}

function handleValue(value) {
  if (typeof value === 'string') {
    return `'${escapeIllegalChars(value)}'`;
  } else if (value instanceof Date) {
    return value.toISOString();
  } else if (value instanceof Number) {
    return value;
  } else if (Array.isArray(value)) {
    // Double quote strings to keep them after `.join`
    const arr = value.map(d => (typeof d === 'string' ? `'${d}'` : d));
    return `[${arr.join(',')}]`;
  } else {
    // TODO: Figure out how best to specify types.  See: https://github.com/devnixs/ODataAngularResources/blob/master/src/odatavalue.js
    switch (value && value.type) {
      case 'guid':
        return value.value;
      case 'raw':
        return value.value;
    }
    return value;
  }
}

function buildExpand(expands) {
  if (typeof expands === 'number') {
    return expands;
  } else if (typeof expands === 'string') {
    if (expands.indexOf('/') === -1) {
      return expands;
    }

    // Change `Foo/Bar/Baz` to `Foo($expand=Bar($expand=Baz))`
    return expands
      .split('/')
      .reverse()
      .reduce((results, item, index, arr) => {
        if (index === 0) {
          // Inner-most item
          return `$expand=${item}`;
        } else if (index === arr.length - 1) {
          // Outer-most item, don't add `$expand=` prefix (added above)
          return `${item}(${results})`;
        } else {
          // Other items
          return `$expand=${item}(${results})`;
        }
      }, '');
  } else if (Array.isArray(expands)) {
    return `${expands.map(e => buildExpand(e)).join(',')}`;
  } else if (typeof expands === 'object') {
    const expandKeys = Object.keys(expands);

    if (
      expandKeys.some(
        key => SUPPORTED_EXPAND_PROPERTIES.indexOf(key.toLowerCase()) !== -1
      )
    ) {
      return expandKeys
        .map(key => {
          const value =
            key === 'filter'
              ? buildFilter(expands[key])
              : key.toLowerCase() === 'orderby'
                ? buildOrderBy(expands[key])
                : buildExpand(expands[key]);
          return `$${key.toLowerCase()}=${value}`;
        })
        .join(';');
    } else {
      return expandKeys
        .map(key => {
          const builtExpand = buildExpand(expands[key]);
          return builtExpand ? `${key}(${builtExpand})` : key;
        })
        .join(',');
    }
  }
}

function buildTransforms(transforms) {
  // Wrap single object an array for simplified processing
  const transformsArray = Array.isArray(transforms) ? transforms : [transforms];

  const transformsResult = transformsArray.reduce((result, transform) => {
    Object.keys(transform).forEach(transformKey => {
      const transformValue = transform[transformKey];
      switch (transformKey) {
        case 'aggregate':
          result.push(`aggregate(${buildAggregate(transformValue)})`);
          break;
        case 'filter':
          const builtFilter = buildFilter(transformValue);
          if (builtFilter !== undefined) {
            result.push(`filter(${buildFilter(transformValue)})`);
          }
          break;
        case 'groupby': // support both cases
        case 'groupBy':
          result.push(`groupby(${buildGroupBy(transformValue)})`);
          break;
        default:
          // TODO: support as many of the following:
          //   topcount, topsum, toppercent,
          //   bottomsum, bottomcount, bottompercent,
          //   identity, concat, expand, search, compute, isdefined
          throw new Error(`Unsupported transform: '${transformKey}'`);
      }
    });

    return result;
  }, []);

  return transformsResult.join('/') || undefined;
}

function buildAggregate(aggregate) {
  // Wrap single object in an array for simplified processing
  const aggregateArray = Array.isArray(aggregate) ? aggregate : [aggregate];

  return aggregateArray
    .map(aggregateItem => {
      return Object.keys(aggregateItem).map(aggregateKey => {
        const aggregateValue = aggregateItem[aggregateKey];

        // TODO: Are these always required?  Can/should we default them if so?
        if (aggregateValue.with === undefined) {
          throw new Error(`'with' property required for '${aggregateKey}'`);
        }
        if (aggregateValue.as === undefined) {
          throw new Error(`'as' property required for '${aggregateKey}'`);
        }

        return `${aggregateKey} with ${aggregateValue.with} as ${
          aggregateValue.as
        }`;
      });
    })
    .join(',');
}

function buildGroupBy(groupBy) {
  if (groupBy.properties === undefined) {
    throw new Error(
      `'properties' property required for groupBy:'${aggregateKey}'`
    );
  }

  let result = `(${groupBy.properties.join(',')})`;

  if (groupBy.transform) {
    result += `,${buildTransforms(groupBy.transform)}`;
  }

  return result;
}

function buildOrderBy(orderBy) {
  if (typeof orderBy === 'number') {
    return orderBy;
  } else if (typeof orderBy === 'string') {
    return orderBy;
  } else if (Array.isArray(orderBy)) {
    return `${orderBy.map(o => buildOrderBy(o)).join(',')}`;
  }
}

function buildUrl(path, params) {
  if (Object.keys(params).length) {
    return (
      path +
      '?' +
      Object.keys(params)
        .map(key => `${key}=${params[key]}`)
        .join('&')
    );
  } else {
    return path;
  }
}

function parseNot(op, builtFilters, parentheses) {
  if (op === 'not') {
    if (builtFilters.length > 1) {
      return `not( ${builtFilters.join(' and ')})`
    } else {
      return builtFilters.map(filter => {
        if (filter.charAt(0) === '(') {
          return '(not '.concat(filter.substr(1))
        } else {
          return 'not '.concat(filter)
        }
      })
    }
  } else {
    if (parentheses) {
      return `(${builtFilters.join(` ${op} `)})`;
    } else {
      return `${builtFilters.join(` ${op} `)}`;
    }
  }
}<commit_msg>Move 'not' logical operator check  from function to main build function<commit_after>const COMPARISON_OPERATORS = ['eq', 'ne', 'gt', 'ge', 'lt', 'le'];
const LOGICAL_OPERATORS = ['and', 'or', 'not'];
const COLLECTION_OPERATORS = ['any', 'all'];
const BOOLEAN_FUNCTIONS = ['startswith', 'endswith', 'contains'];
const SUPPORTED_EXPAND_PROPERTIES = [
  'expand',
  'select',
  'top',
  'orderby',
  'filter'
];

const FUNCTION_REGEX = /\((.*)\)/;

export default function({
  select,
  filter,
  search,
  groupBy,
  transform,
  orderBy,
  top,
  skip,
  key,
  count,
  expand,
  action,
  func,
  format
} = {}) {
  let path = '';
  const params = {};

  if (select) {
    params.$select = select;
  }

  if (filter || count instanceof Object) {
    const builtFilter = buildFilter(count instanceof Object ? count : filter);
    if (builtFilter !== undefined) {
      params.$filter = builtFilter;
    }
  }

  if (search) {
    params.$search = search;
  }

  if (transform) {
    const builtTransforms = buildTransforms(transform);
    if (builtTransforms !== undefined) {
      params.$apply = builtTransforms;
    }
  }

  if (top) {
    params.$top = top;
  }

  if (skip) {
    params.$skip = skip;
  }

  if (key) {
    if (typeof key === 'object') {
      const keys = Object.keys(key)
        .map(k => `${k}=${key[k]}`)
        .join(',');
      path += `(${keys})`;
    } else {
      path += `(${key})`;
    }
  }

  if (count) {
    if (typeof count === 'boolean') {
      params.$count = true;
    } else {
      path += '/$count';
    }
  }

  if (action) {
    path += `/${action}`;
  }

  if (func) {
    if (typeof func === 'string') {
      path += `/${func}`;
    } else if (typeof func === 'object') {
      const [funcName] = Object.keys(func);
      const funcArgs = Object.keys(func[funcName]).reduce(
        (acc, item) => {
          const value = func[funcName][item];
          if (Array.isArray(value) && typeof value[0] === 'object') {
            acc.params.push(`${item}=@${item}`);
            acc.aliases.push(`@${item}=${escape(JSON.stringify(value))}`);
          } else {
            acc.params.push(`${item}=${handleValue(value)}`);
          }
          return acc;
        },
        { params: [], aliases: [] }
      );

      path += `/${funcName}`;
      if (funcArgs.params.length) {
        path += `(${funcArgs.params.join(',')})`;
      }
      if (funcArgs.aliases.length) {
        path += `?${funcArgs.aliases.join(',')}`;
      }
    }
  }

  if (expand) {
    params.$expand = buildExpand(expand);
  }

  if (orderBy) {
    params.$orderby = buildOrderBy(orderBy);
  }

  if(format) {
    params.$format = format;
  }

  return buildUrl(path, params);
}

function buildFilter(filters = {}, propPrefix = '') {
  if (filters == null) {
    // ignore `null` and `undefined` filters (useful for conditionally applied filters)
    return;
  } else if (typeof filters === 'string') {
    // Use raw filter string
    return filters;
  } else if (Array.isArray(filters)) {
    const builtFilters = filters
      .map(f => buildFilter(f, propPrefix))
      .filter(f => f !== undefined);
    if (builtFilters.length) {
      return `${builtFilters.map(f => `(${f})`).join(` and `)}`;
    }
  } else if (typeof filters === 'object') {
    const filtersArray = Object.keys(filters).reduce((result, filterKey) => {
      const value = filters[filterKey];
      const propName = propPrefix
        ? FUNCTION_REGEX.test(filterKey)
          ? filterKey.replace(FUNCTION_REGEX, `(${propPrefix}/$1)`)
          : `${propPrefix}/${filterKey}`
        : filterKey;

      if (
        ['number', 'string', 'boolean'].indexOf(typeof value) !== -1 ||
        value instanceof Date ||
        value === null
      ) {
        // Simple key/value handled as equals operator
        result.push(`${propName} eq ${handleValue(value)}`);
      } else if (Array.isArray(value)) {
        const op = filterKey;
        const builtFilters = value
          .map(v => buildFilter(v, propPrefix))
          .filter(f => f !== undefined)
          .map(f => (LOGICAL_OPERATORS.indexOf(op) !== -1 ? `(${f})` : f));
        if (builtFilters.length) {
          if (LOGICAL_OPERATORS.indexOf(op) !== -1) {
            if (builtFilters.length) {
              if (op === 'not') {
                result.push(parseNot(op, builtFilters));
              }else{
                result.push(`(${builtFilters.join(` ${op} `)})`)
              }
              }
          } else {
                result.push(builtFilters.join(` ${op} `));
          }
        }
      } else if (LOGICAL_OPERATORS.indexOf(propName) !== -1) {
        const op = propName;
        const builtFilters = Object.keys(value).map(valueKey =>
          buildFilter({ [valueKey]: value[valueKey] })
        );
        if (builtFilters.length) {
          if (op === 'not') {
            result.push(parseNot(op, builtFilters));

          }else{
            result.push(`${builtFilters.join(` ${op} `)}`)
          }
        }
      } else if (value instanceof Object) {
        if ('type' in value) {
          result.push(`${propName} eq ${handleValue(value)}`);
        } else {
          const operators = Object.keys(value);
          operators.forEach(op => {
            if (COMPARISON_OPERATORS.indexOf(op) !== -1) {
              result.push(`${propName} ${op} ${handleValue(value[op])}`);
            } else if (LOGICAL_OPERATORS.indexOf(op) !== -1) {
              if (Array.isArray(value[op])) {
                result.push(
                  value[op]
                    .map(v => '(' + buildFilter(v, propName) + ')')
                    .join(` ${op} `)
                );
              } else {
                result.push('(' + buildFilter(value[op], propName) + ')');
              }
            } else if (COLLECTION_OPERATORS.indexOf(op) !== -1) {
              const lambaParameter = filterKey[0].toLowerCase();
              const filter = buildFilter(value[op], lambaParameter);

              if (filter !== undefined) {
                // Do not apply collection filter if undefined (ex. ignore `Foo: { any: {} }`)
                result.push(`${propName}/${op}(${lambaParameter}:${filter})`);
              }
            } else if (op === 'in') {
              const resultingValues = Array.isArray(value[op])
                ? // Convert `{ Prop: { in: [1,2,3] } }` to `(Prop eq 1 or Prop eq 2 or Prop eq 3)`
                  value[op]
                : // Convert `{ Prop: { in: [{type: type, value: 1},{type: type, value: 2},{type: type, value: 3}] } }`
                  // to `(Prop eq 1 or Prop eq 2 or Prop eq 3)`
                  value[op].value.map(typedValue => ({
                    type: value[op].type,
                    value: typedValue
                  }));

              result.push(
                '(' +
                  resultingValues
                    .map(v => `${propName} eq ${handleValue(v)}`)
                    .join(' or ') +
                  ')'
              );
            } else if (BOOLEAN_FUNCTIONS.indexOf(op) !== -1) {
              // Simple boolean functions (startswith, endswith, contains)
              result.push(`${op}(${propName},${handleValue(value[op])})`);
            } else {
              // Nested property
              result.push(buildFilter(value, propName));
            }
          });
        }
      } else if (value === undefined) {
        // Ignore/omit filter if value is `undefined`
      } else {
        throw new Error(`Unexpected value type: ${value}`);
      }

      return result;
    }, []);

    return filtersArray.join(' and ') || undefined;
  } else {
    throw new Error(`Unexpected filters type: ${filters}`);
  }
}

function escapeIllegalChars(string) {
  string = string.replace(/%/g, '%25');
  string = string.replace(/\+/g, '%2B');
  string = string.replace(/\//g, '%2F');
  string = string.replace(/\?/g, '%3F');
  string = string.replace(/#/g, '%23');
  string = string.replace(/&/g, '%26');
  string = string.replace(/'/g, "''");
  return string;
}

function handleValue(value) {
  if (typeof value === 'string') {
    return `'${escapeIllegalChars(value)}'`;
  } else if (value instanceof Date) {
    return value.toISOString();
  } else if (value instanceof Number) {
    return value;
  } else if (Array.isArray(value)) {
    // Double quote strings to keep them after `.join`
    const arr = value.map(d => (typeof d === 'string' ? `'${d}'` : d));
    return `[${arr.join(',')}]`;
  } else {
    // TODO: Figure out how best to specify types.  See: https://github.com/devnixs/ODataAngularResources/blob/master/src/odatavalue.js
    switch (value && value.type) {
      case 'guid':
        return value.value;
      case 'raw':
        return value.value;
    }
    return value;
  }
}

function buildExpand(expands) {
  if (typeof expands === 'number') {
    return expands;
  } else if (typeof expands === 'string') {
    if (expands.indexOf('/') === -1) {
      return expands;
    }

    // Change `Foo/Bar/Baz` to `Foo($expand=Bar($expand=Baz))`
    return expands
      .split('/')
      .reverse()
      .reduce((results, item, index, arr) => {
        if (index === 0) {
          // Inner-most item
          return `$expand=${item}`;
        } else if (index === arr.length - 1) {
          // Outer-most item, don't add `$expand=` prefix (added above)
          return `${item}(${results})`;
        } else {
          // Other items
          return `$expand=${item}(${results})`;
        }
      }, '');
  } else if (Array.isArray(expands)) {
    return `${expands.map(e => buildExpand(e)).join(',')}`;
  } else if (typeof expands === 'object') {
    const expandKeys = Object.keys(expands);

    if (
      expandKeys.some(
        key => SUPPORTED_EXPAND_PROPERTIES.indexOf(key.toLowerCase()) !== -1
      )
    ) {
      return expandKeys
        .map(key => {
          const value =
            key === 'filter'
              ? buildFilter(expands[key])
              : key.toLowerCase() === 'orderby'
                ? buildOrderBy(expands[key])
                : buildExpand(expands[key]);
          return `$${key.toLowerCase()}=${value}`;
        })
        .join(';');
    } else {
      return expandKeys
        .map(key => {
          const builtExpand = buildExpand(expands[key]);
          return builtExpand ? `${key}(${builtExpand})` : key;
        })
        .join(',');
    }
  }
}

function buildTransforms(transforms) {
  // Wrap single object an array for simplified processing
  const transformsArray = Array.isArray(transforms) ? transforms : [transforms];

  const transformsResult = transformsArray.reduce((result, transform) => {
    Object.keys(transform).forEach(transformKey => {
      const transformValue = transform[transformKey];
      switch (transformKey) {
        case 'aggregate':
          result.push(`aggregate(${buildAggregate(transformValue)})`);
          break;
        case 'filter':
          const builtFilter = buildFilter(transformValue);
          if (builtFilter !== undefined) {
            result.push(`filter(${buildFilter(transformValue)})`);
          }
          break;
        case 'groupby': // support both cases
        case 'groupBy':
          result.push(`groupby(${buildGroupBy(transformValue)})`);
          break;
        default:
          // TODO: support as many of the following:
          //   topcount, topsum, toppercent,
          //   bottomsum, bottomcount, bottompercent,
          //   identity, concat, expand, search, compute, isdefined
          throw new Error(`Unsupported transform: '${transformKey}'`);
      }
    });

    return result;
  }, []);

  return transformsResult.join('/') || undefined;
}

function buildAggregate(aggregate) {
  // Wrap single object in an array for simplified processing
  const aggregateArray = Array.isArray(aggregate) ? aggregate : [aggregate];

  return aggregateArray
    .map(aggregateItem => {
      return Object.keys(aggregateItem).map(aggregateKey => {
        const aggregateValue = aggregateItem[aggregateKey];

        // TODO: Are these always required?  Can/should we default them if so?
        if (aggregateValue.with === undefined) {
          throw new Error(`'with' property required for '${aggregateKey}'`);
        }
        if (aggregateValue.as === undefined) {
          throw new Error(`'as' property required for '${aggregateKey}'`);
        }

        return `${aggregateKey} with ${aggregateValue.with} as ${
          aggregateValue.as
        }`;
      });
    })
    .join(',');
}

function buildGroupBy(groupBy) {
  if (groupBy.properties === undefined) {
    throw new Error(
      `'properties' property required for groupBy:'${aggregateKey}'`
    );
  }

  let result = `(${groupBy.properties.join(',')})`;

  if (groupBy.transform) {
    result += `,${buildTransforms(groupBy.transform)}`;
  }

  return result;
}

function buildOrderBy(orderBy) {
  if (typeof orderBy === 'number') {
    return orderBy;
  } else if (typeof orderBy === 'string') {
    return orderBy;
  } else if (Array.isArray(orderBy)) {
    return `${orderBy.map(o => buildOrderBy(o)).join(',')}`;
  }
}

function buildUrl(path, params) {
  if (Object.keys(params).length) {
    return (
      path +
      '?' +
      Object.keys(params)
        .map(key => `${key}=${params[key]}`)
        .join('&')
    );
  } else {
    return path;
  }
}

function parseNot(op, builtFilters) {
    if (builtFilters.length > 1) {
      return `not( ${builtFilters.join(' and ')})`
    } else {
      return builtFilters.map(filter => {
        if (filter.charAt(0) === '(') {
          return '(not '.concat(filter.substr(1))
        } else {
          return 'not '.concat(filter)
        }
      })
    }
}<|endoftext|>


<commit_before>const EventEmitter = require("eventemitter3");

class CircleSlider extends EventEmitter {
  /**
   * Creates an instance of CircleSlider inside the element with the id `targetId`
   * @param {String} targetId              The id of the element to contain the circle slider.
   * @param {Object} [options]             An object containing options for the slider.
   * @param {Number} [options.snap]        Makes the handle snap to every multiple of this number.
   * @param {Boolean} [options.clockwise]  True to make clockwise the positive direction.
   * @param {"top"|"bottom"|"left"|"right"} [options.startPos]
   *    Which side the handle should start at.
   * @memberof CircleSlider
   */
  constructor(targetId, options) {
    super();
    // allow both "id" or "#id"
    this.root = document.getElementById(targetId) || document.getElementById(targetId.slice(1));
    this.outputAngle = 0;
    this.direction = options.dir;
    this.clockwise = options.clockwise;
    this.snapMultiplier = options.snap;

    // validation
    if (!this.root) {
      console.error(`CircleSlider: Didn't find any element with id ${targetId}`);
    }

    // create the child elements and append them
    this.hc = CircleSlider._createHandleContainerElem();
    this.handle = CircleSlider._createHandleElem();
    this.hc.appendChild(this.handle);
    this.root.appendChild(this.hc);

    // just to keep track of all event names
    this.events = {
      sliderMove: "sliderMove",
      sliderUp: "sliderUp",
    };

    // active is true when user is holding down handle
    this.active = false;
    // mouse events
    this._addEventListeners("mousedown", "mousemove", "mouseup");
    // touch events
    this._addEventListeners("touchstart", "touchmove", "touchend");

    // bind methods
    this._mouseMoveHandler = this._mouseMoveHandler.bind(this);
  }

  // public methods

  /**
   * Returns the angle/value of the slider.
   *
   * @returns The current value
   * @memberof CircleSlider
   */
  getAngle() {
    return this.outputAngle;
  }

  /**
   * Manually sets the angle/value of the slider.
   *
   * @param {Number} angle  The new value for the slider
   * @memberof CircleSlider
   */
  setAngle(angle) {
    const rawAngle = CircleSlider._formatOutputAngle(angle);
    this._moveHandle(rawAngle);
  }

  // "private" methods

  _addEventListeners(startEvent, moveEvent, endEvent) {
    // user presses handle
    this.handle.addEventListener(startEvent, (e) => {
      // prevent text selection
      e.preventDefault();

      if (!this.active) {
        this.active = true;

        // user moves handle
        document.addEventListener(moveEvent, this._mouseMoveHandler, false);

        // user lets go
        document.addEventListener(endEvent, () => {
          this.active = false;
          document.removeEventListener(moveEvent, this._mouseMoveHandler, false);
          this.emit(this.events.sliderUp, this.outputAngle);
        });
      }
    });
  }

  _mouseMoveHandler(e) {
    e.preventDefault();
    this._moveHandle(this._getRawAngle(e));
  }

  _moveHandle(rawAngle) {
    let angle = rawAngle;
    // snap handle to multiples of snapMultiplier
    if (this.snapMultiplier) {
      const sm = this.snapMultiplier;
      const delta = Math.abs(angle - (Math.round(angle / sm) * sm));
      if (delta <= 5) {
        angle = Math.round(angle / sm) * sm;
      }
    }

    // move the handle visually
    this.hc.style.cssText = `transform: rotate(${angle}deg);`;

    this.outputAngle = CircleSlider._formatOutputAngle(angle);

    this.emit(this.events.sliderMove, this.outputAngle);
  }

  static _formatOutputAngle(angle) {
    const outputAngle = this.clockwise ?
      (360 - Math.round(angle)) % 360 :
      (360 + Math.round(angle)) % 360;
    return outputAngle;
  }

  _getRawAngle(e) {
    const pivot = CircleSlider._getCenter(this.root);
    const mouse = {
      x: e.pageX,
      y: e.pageY,
    };
    const offset = -90;
    let angle = CircleSlider._radToDeg(Math.atan2(mouse.x - pivot.x, -(mouse.y - pivot.y)))
     + offset;
    if (angle < 0) angle += 360;
    return angle;
  }

  static _getCenter(elem) {
    const rect = elem.getBoundingClientRect();
    return {
      x: rect.left + (rect.width / 2),
      y: rect.top + (rect.height / 2),
    };
  }

  static _radToDeg(rad) {
    return rad * (180 / Math.PI);
  }

  // Uninteresting methods

  static _createHandleContainerElem() {
    const hc = document.createElement("div");
    hc.className = "cs-handle-container";
    return hc;
  }

  static _createHandleElem() {
    const h = document.createElement("div");
    h.className = "cs-handle";
    return h;
  }
}

module.exports = CircleSlider;
<commit_msg>🚧 Fix bug in _formatOutputAngle<commit_after>const EventEmitter = require("eventemitter3");

class CircleSlider extends EventEmitter {
  /**
   * Creates an instance of CircleSlider inside the element with the id `targetId`
   * @param {String} targetId              The id of the element to contain the circle slider.
   * @param {Object} [options]             An object containing options for the slider.
   * @param {Number} [options.snap]        Makes the handle snap to every multiple of this number.
   * @param {Boolean} [options.clockwise]  True to make clockwise the positive direction.
   * @param {"top"|"bottom"|"left"|"right"} [options.startPos]
   *    Which side the handle should start at.
   * @memberof CircleSlider
   */
  constructor(targetId, options) {
    super();
    // allow both "id" or "#id"
    this.root = document.getElementById(targetId) || document.getElementById(targetId.slice(1));
    this.outputAngle = 0;
    this.clockwise = options.clockwise; // affects _formatOutputAngle
    this.snapMultiplier = options.snap;

    // validation
    if (!this.root) {
      console.error(`CircleSlider: Didn't find any element with id ${targetId}`);
    }

    // create the child elements and append them
    this.hc = CircleSlider._createHandleContainerElem();
    this.handle = CircleSlider._createHandleElem();
    this.hc.appendChild(this.handle);
    this.root.appendChild(this.hc);

    // just to keep track of all event names
    this.events = {
      sliderMove: "sliderMove",
      sliderUp: "sliderUp",
    };

    // active is true when user is holding down handle
    this.active = false;
    // mouse events
    this._addEventListeners("mousedown", "mousemove", "mouseup");
    // touch events
    this._addEventListeners("touchstart", "touchmove", "touchend");

    // bind methods
    this._mouseMoveHandler = this._mouseMoveHandler.bind(this);
  }

  // public methods

  /**
   * Returns the angle/value of the slider.
   *
   * @returns The current value
   * @memberof CircleSlider
   */
  getAngle() {
    return this.outputAngle;
  }

  /**
   * Manually sets the angle/value of the slider.
   *
   * @param {Number} angle  The new value for the slider
   * @memberof CircleSlider
   */
  setAngle(angle) {
    const rawAngle = this._formatOutputAngle(angle);
    this._moveHandle(rawAngle);
  }

  // "private" methods

  _addEventListeners(startEvent, moveEvent, endEvent) {
    // user presses handle
    this.handle.addEventListener(startEvent, (e) => {
      // prevent text selection
      e.preventDefault();

      if (!this.active) {
        this.active = true;

        // user moves handle
        document.addEventListener(moveEvent, this._mouseMoveHandler, false);

        // user lets go
        document.addEventListener(endEvent, () => {
          this.active = false;
          document.removeEventListener(moveEvent, this._mouseMoveHandler, false);
          this.emit(this.events.sliderUp, this.outputAngle);
        });
      }
    });
  }

  _mouseMoveHandler(e) {
    e.preventDefault();
    this._moveHandle(this._getRawAngle(e));
  }

  _moveHandle(rawAngle) {
    let angle = rawAngle;
    // snap handle to multiples of snapMultiplier
    if (this.snapMultiplier) {
      const sm = this.snapMultiplier;
      const delta = Math.abs(angle - (Math.round(angle / sm) * sm));
      if (delta <= 5) {
        angle = Math.round(angle / sm) * sm;
      }
    }

    // move the handle visually
    this.hc.style.cssText = `transform: rotate(${angle}deg);`;

    this.outputAngle = this._formatOutputAngle(angle);

    this.emit(this.events.sliderMove, this.outputAngle);
  }

  _formatOutputAngle(angle) {
    const outputAngle = this.clockwise === true ?
      (360 - Math.round(angle)) % 360 :
      (360 + Math.round(angle)) % 360;
    console.log(this.clockwise);
    return outputAngle;
  }

  _getRawAngle(e) {
    const pivot = CircleSlider._getCenter(this.root);
    const mouse = {
      x: e.pageX,
      y: e.pageY,
    };
    const offset = -90;
    let angle = CircleSlider._radToDeg(Math.atan2(mouse.x - pivot.x, -(mouse.y - pivot.y)))
     + offset;
    if (angle < 0) angle += 360;
    return angle;
  }

  static _getCenter(elem) {
    const rect = elem.getBoundingClientRect();
    return {
      x: rect.left + (rect.width / 2),
      y: rect.top + (rect.height / 2),
    };
  }

  static _radToDeg(rad) {
    return rad * (180 / Math.PI);
  }

  // Uninteresting methods

  static _createHandleContainerElem() {
    const hc = document.createElement("div");
    hc.className = "cs-handle-container";
    return hc;
  }

  static _createHandleElem() {
    const h = document.createElement("div");
    h.className = "cs-handle";
    return h;
  }
}

module.exports = CircleSlider;
<|endoftext|>


<commit_before>'use strict';

var polyfills = require('famous-polyfills');
var rAF = polyfills.requestAnimationFrame;

function Engine () {
    this.updates = [];
    var _this = this;
    this.looper = function(time) {
        _this.loop(time);
    };
    this.looper();
}

Engine.prototype.step = function step (time) {
    for (var i = 0, len = this.updates.length ; i < len ; i++) {
        this.updates[i].update(time);
    }
};

Engine.prototype.loop = function loop (time) {
    this.step(time);
    rAF(this.looper);
};

Engine.prototype.update = function update (item) {
    this.updates.push(item);
    return this;
};

Engine.prototype.noLongerUpdate = function noLongerUpdate (item) {
    this.updates.splice(this.updates.indexOf(item), 1);
    return this;
};

module.exports = Engine;
<commit_msg>feat: Add isRunning, start and stop method<commit_after>'use strict';

var polyfills = require('famous-polyfills');
var rAF = polyfills.requestAnimationFrame;

function Engine() {
    this._updates = [];
    var _this = this;
    this.looper = function(time) {
        _this.loop(time);
    };
    this._looper = this.loop.bind(this);
    this.start();
}

Engine.prototype.start = function start() {
    this._running = true;
    this._looper();
    return this;
};

Engine.prototype.stop = function stop() {
    this._running = false;
    return this;
};

Engine.prototype.isRunning = function isRunning() {
    return this._running;
};

Engine.prototype.step = function step (time) {
    for (var i = 0, len = this._updates.length ; i < len ; i++) {
        this._updates[i].update(time);
    }
    return this;
};

Engine.prototype.loop = function loop(time) {
    this.step(time);
    if (this._running) {
        rAF(this._looper);
    }
    return this;
};

Engine.prototype.update = function update(item) {
    this._updates.push(item);
    return this;
};

Engine.prototype.noLongerUpdate = function noLongerUpdate(item) {
    this._updates.splice(this._updates.indexOf(item), 1);
    return this;
};

module.exports = Engine;
<|endoftext|>


<commit_before>import EventEmitter from 'events';
import { Client } from 'node-ssdp';

import Yeelight from './Yeelight';

/**
 * Create a new instance of the YeelightSearch class
 * and start searching for new Yeelights
 * once a Yeelight has been found it will create an Yeelight instance
 * and emits the 'found' event light the Yeelight instance as payload
 *
 * @extends EventEmitter
 */
class YeelightSearch extends EventEmitter {
  constructor() {
    super();

    this.yeelights = [];
    // Setting the sourcePort ensures multicast traffic is received
    this.client = new Client({ sourcePort: 1982, ssdpPort: 1982 });

    this.client.on('response', (data) => this.addLight(data));
    // Register devices that sends NOTIFY to multicast address too
    this.client.on('advertise-alive', (data) => this.addLight(data));

    this.client.search('wifi_bulb');
  }

  /**
   * adds a new light to the lights array
   */
  addLight(lightdata) {
    let yeelight = this.yeelights.find(item => item.getId() === lightdata.ID);
    if (!yeelight) {
	  yeelight = new Yeelight(lightdata);
	  this.yeelights.push(yeelight);
	  this.emit('found', yeelight);
    }
  }

  /**
   * returns a list of all found Yeelights
   * @returns {array.<Yeelight>} array with yeelight instances
   */
  getYeelights() {
    return this.yeelights;
  }

  /**
   * returns one Yeelight found by id
   * @param {string} id Yeelight Id
   * @returns {Yeelight} Yeelight instance
   */
  getYeelightById(id) {
    return this.yeelights.find(item => item.getId() === id);
  }
}

module.exports = YeelightSearch;
<commit_msg>Add a refresh function<commit_after>import EventEmitter from 'events';
import { Client } from 'node-ssdp';

import Yeelight from './Yeelight';

/**
 * Create a new instance of the YeelightSearch class
 * and start searching for new Yeelights
 * once a Yeelight has been found it will create an Yeelight instance
 * and emits the 'found' event light the Yeelight instance as payload
 *
 * @extends EventEmitter
 */
class YeelightSearch extends EventEmitter {
  constructor() {
    super();

    this.yeelights = [];
    // Setting the sourcePort ensures multicast traffic is received
    this.client = new Client({ sourcePort: 1982, ssdpPort: 1982 });

    this.client.on('response', (data) => this.addLight(data));
    // Register devices that sends NOTIFY to multicast address too
    this.client.on('advertise-alive', (data) => this.addLight(data));

    this.client.search('wifi_bulb');
  }

  /**
   * adds a new light to the lights array
   */
  addLight(lightdata) {
    let yeelight = this.yeelights.find(item => item.getId() === lightdata.ID);
    if (!yeelight) {
	  yeelight = new Yeelight(lightdata);
	  this.yeelights.push(yeelight);
	  this.emit('found', yeelight);
    }
  }

  /**
   * returns a list of all found Yeelights
   * @returns {array.<Yeelight>} array with yeelight instances
   */
  getYeelights() {
    return this.yeelights;
  }

  /**
   * returns one Yeelight found by id
   * @param {string} id Yeelight Id
   * @returns {Yeelight} Yeelight instance
   */
  getYeelightById(id) {
    return this.yeelights.find(item => item.getId() === id);
  }

  /**
   * refresh lights sending a new m-search
   */
  refresh() {
    this.client.search('wifi_bulb');
  }
}

module.exports = YeelightSearch;
<|endoftext|>


<commit_before>var fs = require('fs')
var path = require('path')
var exec = require('child_process').exec
var normalize = require('normalize-path')

module.exports = {
  isHusky: function (filename) {
    var data = fs.readFileSync(filename, 'utf-8')
    return data.indexOf('# husky') !== -1
  },

  hooksDir: function (callback) {
    exec('git rev-parse --git-dir', function (error, stdout, stderr) {
      if (error) {
        callback(stderr, null)
      } else {
        callback(null, stdout.trim() + '/hooks')
      }
    })
  },

  write: function (filename, data) {
    fs.writeFileSync(filename, data)
    fs.chmodSync(filename, 0755)
  },

  create: function (dir, name, cmd) {
    var filename = dir + '/' + name
    var arr = [
      '#!/bin/sh',
      '# husky'
    ]

    // Assuming that this file is in node_modules/husky/src
    var packageDir = path.join(__dirname, '..', '..', '..')

    // dir being .git/hooks
    var projectDir = path.join(dir, '..', '..')

    // In order to support projects with package.json in a different directory
    // than .git, find relative path from project directory to package.json
    var relativePath = path.join('.', path.relative(projectDir, packageDir))

    // On Windows normalize path (i.e. convert \ to /)
    var normalizedPath = normalize(relativePath)

    // Hook script
    arr = arr.concat([
      'cd ' + normalizedPath,

      // Fix for issue #16 #24
      // Test if script is defined in package.json
      '[ -f package.json ] && cat package.json | grep -q \'"' + cmd + '"\\s*:\'',
      // package.json or script can't be found exit
      '[ $? -ne 0 ] && exit 0',
    ])

    // On OS X and Linux, try to use nvm if it's installed
    if (process.platform !== 'win32') {
      // ~ is unavaible, so $HOME is used
      var home = process.env.HOME

      // If nvm is installed, try to load it
      // This will load default Node version or version specified by .nvmrc
      arr = arr.concat([
        'export NVM_DIR="' + home + '/.nvm"',
        '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && [ -f .nvmrc ] && nvm use',
      ])
    }

    // Can't find npm message
    var npmNotFound = 'husky - can\'t find npm in PATH. Skipping ' + cmd + ' script in package.json'

    arr = arr.concat([
      // Test if npm is in PATH
      'command -v npm >/dev/null 2>&1 || { echo >&2 "' + npmNotFound + '"; exit 0; }',

      // Run script
      'npm run ' + cmd,
      'if [ $? -ne 0 ]; then',
      '  echo',
      '  echo "husky - ' + name + ' hook failed (add --no-verify to bypass)"',
      '  echo',
      '  exit 1',
      'fi',
      ''
    ])

    // Create hooks directory if needed
    if (!fs.existsSync(dir)) fs.mkdirSync(dir)

    // Create hook file
    var data = arr.join('\n')
    if (!fs.existsSync(filename)) {
      this.write(filename, data)
    } else {
      if (this.isHusky(filename)) {
        this.write(filename, data)
      } else {
        console.log('skipping .git/hooks/' + name + ' (existing user hook)')
      }
    }
  },

  remove: function (dir, name) {
    var filename = dir + '/' + name

    if (fs.existsSync(filename) && this.isHusky(filename)) {
      fs.unlinkSync(dir + '/' + name)
    }
  }
}
<commit_msg>Support nvm installed via homebrew<commit_after>var fs = require('fs')
var path = require('path')
var exec = require('child_process').exec
var normalize = require('normalize-path')

module.exports = {
  isHusky: function (filename) {
    var data = fs.readFileSync(filename, 'utf-8')
    return data.indexOf('# husky') !== -1
  },

  hooksDir: function (callback) {
    exec('git rev-parse --git-dir', function (error, stdout, stderr) {
      if (error) {
        callback(stderr, null)
      } else {
        callback(null, stdout.trim() + '/hooks')
      }
    })
  },

  write: function (filename, data) {
    fs.writeFileSync(filename, data)
    fs.chmodSync(filename, 0755)
  },

  create: function (dir, name, cmd) {
    var filename = dir + '/' + name
    var arr = [
      '#!/bin/sh',
      '# husky'
    ]

    // Assuming that this file is in node_modules/husky/src
    var packageDir = path.join(__dirname, '..', '..', '..')

    // dir being .git/hooks
    var projectDir = path.join(dir, '..', '..')

    // In order to support projects with package.json in a different directory
    // than .git, find relative path from project directory to package.json
    var relativePath = path.join('.', path.relative(projectDir, packageDir))

    // On Windows normalize path (i.e. convert \ to /)
    var normalizedPath = normalize(relativePath)

    // Hook script
    arr = arr.concat([
      'cd ' + normalizedPath,

      // Fix for issue #16 #24
      // Test if script is defined in package.json
      '[ -f package.json ] && cat package.json | grep -q \'"' + cmd + '"\\s*:\'',
      // package.json or script can't be found exit
      '[ $? -ne 0 ] && exit 0',
    ])

    // On OS X and Linux, try to use nvm if it's installed
    if (process.platform !== 'win32') {
      // ~ is unavaible, so $HOME is used
      var home = process.env.HOME

      // This will load default Node version or version specified by .nvmrc
      arr = arr.concat([
        'export NVM_DIR="' + home + '/.nvm"'
      ])

      if (process.platform === 'darwin') {
        // If nvm was installed using homebrew,
        // nvm script will be found in /usr/local/opt/nvm
        arr = arr.concat([
          'BREW_NVM_DIR="/usr/local/opt/nvm"',
          '[ -s "$BREW_NVM_DIR/nvm.sh" ] && . "$BREW_NVM_DIR/nvm.sh"'
        ])
      }

      arr = arr.concat([
        // If nvm was installed using install script,
        // nvm script will be found in $NVM_DIR
        '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"',
        // Test if nvm is in PATH and load version specified by .nvmrc
        'command -v nvm >/dev/null 2>&1 && [ -f .nvmrc ] && nvm use'
      ])
    }

    // Can't find npm message
    var npmNotFound = 'husky - can\'t find npm in PATH. Skipping ' + cmd + ' script in package.json'

    arr = arr.concat([
      // Test if npm is in PATH
      'command -v npm >/dev/null 2>&1 || { echo >&2 "' + npmNotFound + '"; exit 0; }',

      // Run script
      'npm run ' + cmd,
      'if [ $? -ne 0 ]; then',
      '  echo',
      '  echo "husky - ' + name + ' hook failed (add --no-verify to bypass)"',
      '  echo',
      '  exit 1',
      'fi',
      ''
    ])

    // Create hooks directory if needed
    if (!fs.existsSync(dir)) fs.mkdirSync(dir)

    // Create hook file
    var data = arr.join('\n')
    if (!fs.existsSync(filename)) {
      this.write(filename, data)
    } else {
      if (this.isHusky(filename)) {
        this.write(filename, data)
      } else {
        console.log('skipping .git/hooks/' + name + ' (existing user hook)')
      }
    }
  },

  remove: function (dir, name) {
    var filename = dir + '/' + name

    if (fs.existsSync(filename) && this.isHusky(filename)) {
      fs.unlinkSync(dir + '/' + name)
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

const regression = require('./regression');
const fs = require('fs');
const FreeSurfer = require('freesurfer-parser');
const get = require('lodash/get');
const pkg = require('../package.json');
const n = require('numeric');

/**
 * Get FreeSurfer region of interest picker.
 *
 * @param {string} feature
 * @returns {Function} FreeSurfer data picker
 */
/* eslint-disable arrow-parens,arrow-body-style */
const getFreeSurferDataPicker = feature => freeSurferData => {
  return new FreeSurfer({ string: freeSurferData })[feature];
};
/* eslint-enable arrow-parens,arrow-body-style */

/**
 * Read file.
 *
 * @param {string} file
 * @returns {Promise} Resolves to `file`'s contents
 */
const readFileAsync = file => new Promise((resolve, reject) => {
  fs.readFile(file, (error, data) => {
    if (error) {
      reject(error);
    } else {
      resolve(data.toString());
    }
  });
});

/**
 * Get normalized tags.
 *
 * @param {Object} file
 * @param {Object} file.tags Hash map of tag names to values
 * @returns {Array} Normalized tags
 */
const getNormalizedTags = file => Object.keys(file.tags).sort().reduce(
  (memo, tag) => {
    const value = file.tags[tag];

    if (typeof value === 'boolean') {
      return memo.concat(value === true ? 1 : -1);
    } else if (typeof value === 'number') {
      return memo.concat(value);
    }

    return memo;
  },
  []
);

module.exports = {
  name: pkg.name,
  version: pkg.version,
  local: [{
    type: 'function',
    fn(opts) {
      let features = get(opts, 'remoteResult.pluginState.inputs[0][0]');
      const files = get(opts, 'userData.files');

      // TODO: This is a hack for simulator. Figure out how to load plugin state
      /* eslint-disable no-underscore-dangle */
      if (!features && opts.userData.__FEATURES__) {
        features = opts.userData.__FEATURES__;
      }
      /* eslint-enable no-underscore-dangle */

      if (!Array.isArray(features) || !features.length) {
        return Promise.reject(new Error('Expected RoI features inputs'));
      } else if (features.some(f => FreeSurfer.validFields.indexOf(f) < 0)) {
        return Promise.reject(new Error(
          `Invalid FreeSurfer feature in ${features.toString()}`
        ));
      } else if (!Array.isArray(files) || !files.length) {
        return Promise.reject(new Error('Expected user data to contain files'));
      }

      const pickFeature = getFreeSurferDataPicker(features[0]);

      return Promise.all(files.map(f => readFileAsync(f.filename)))
        .then(freeSurferDatas => { // eslint-disable-line arrow-parens
          const x = files.map(getNormalizedTags); // covariates
          const y = freeSurferDatas.map(pickFeature); // RoIs

          // calculate regression result and localMeanY
          const biasedX = x.map(covariates => [1].concat(covariates));
          const localCount = y.length;
          const betaVector = regression.oneShot(biasedX, y);
          const rSquared = regression.rSquared(biasedX, y, betaVector);
          const tValue = regression.tValue(biasedX, y, betaVector);
          const localMeanY = n.sum(y) / localCount;

          /* eslint-disable no-console */
          console.log('X is:', biasedX);
          console.log('y is:', y);
          console.log('beta vector is:', betaVector);
          console.log('local r square of fitting is:', rSquared);
          console.log('local tValues of betaVector are:', tValue);
          /* eslint-enable no-console */

          return {
            betaVector,
            localCount,
            localMeanY,
            rSquared,
            tValue,
          };
        });
    },
    inputs: [{
      help: 'Select Freesurfer region(s) of interest',
      label: 'Freesurfer ROI',
      type: 'select',
      values: FreeSurfer.validFields,
    }, {
      label: 'Covariates',
      type: 'covariates',
    }],
  }, {
    // step two: receive the globalMeanY and averageBetaVector, then calculate sseLocal, sstLocal and varXLocal
    type: 'function',
    fn(opts) {
      const globalMeanY = opts.remoteResult.data.globalMeanY;
      const averageBetaVector = opts.remoteResult.data.averageBetaVector;
      const biasedX = opts.previousData.biasedX;
      const y = opts.previousData.y;
      const localCount = y.length;

      // calculate sseLocal and sstLocal
      const sseLocal=n.sum(n.pow(n.sub(y, n.dot(biasedX, averageBetaVector)), 2));
      const sstLocal=n.sum(n.pow(n.sub(y, n.rep(n.dim(y), globalMeanY)), 2));

      // calculate varXLocal

      const varXLocalMatrix=n.dot(n.transpose(biasedX),biasedX);
      const varXLocal=[];
      for (let i=0; i<averageBetaVector.length; i += 1) {
         varXLocal.push(varXLocalMatrix[i][i]);
      }

      /* eslint-disable no-console */
      console.log('globalMeanY:', globalMeanY);
      console.log('opts is:', opts);
      /* eslint-enable no-console */

      return {
        sseLocal;
        sstLocal;
        varXLocal;
        averageBetaVector;
        localCount;
        };
    },

  }],
  remote: [{
    type: 'function',
    fn(opts) {
      const userResults = opts.userResults;

      // Not all user results contain betas. Return early.
      if (userResults.some(userResult => !((userResult || {}).data || {}).betaVector)) {
        return {};
      }

      // calculate averageBetaVector
      const averageBetaVector = [];
      const betaCount = userResults[0].data.betaVector.length;
      const userCount = userResults.length;

      for (let i = 0; i < betaCount; i += 1) {
        averageBetaVector.push(userResults.reduce(
          (sum, userResult) => sum + userResult.data.betaVector[i], 0
        ) / userCount);
      }

      // calculate globalMeanY
      const siteCount=userResults.length;
      var totalY=0;
      var globalYCount=0;

      for (let i=0; i < siteCount; i += 1) {
         totalY += userResults[i].data.localMeanY*userResults[i].data.localCount;
         globalYCount += userResults[i].data.localCount;
      }

      const globalMeanY = totalY/globalYCount;

      /* eslint-disable no-console */
      console.log('Average beta vector:', averageBetaVector);
      console.log('globalMeanY is :', userResults[0]);
      /* eslint-enable no-console */

      return {
        averageBetaVector,
        globalMeanY,
//        complete: true,
      };
    },
    verbose: true,
  }, {
    // get sstNode from each local node and calculate the statistics
    type: 'function',
    fn(opts) {
    // console.log('opts.userResults:',opts.userResults[0]);
    // calcuate globalRSquared
    const userResults = opts.userResults;
    const sseGlobal = userResults.reduce((sum, userResult) => sum + userResult.data.sseLocal, 0);

      const sstNode1 = opts.userResults[0].data.sstNode;

      return {
        averageBetaVector;
        rSquared;
        tValue;,
        complete: true,
      };
    },
    verbose: true,
  }],
  plugins: ['group-step', 'inputs'],
};
<commit_msg>add computation complete<commit_after>'use strict';

const regression = require('./regression');
const fs = require('fs');
const FreeSurfer = require('freesurfer-parser');
const get = require('lodash/get');
const pkg = require('../package.json');
const n=require('numeric');

/**
 * Get FreeSurfer region of interest picker.
 *
 * @param {string} feature
 * @returns {Function} FreeSurfer data picker
 */
/* eslint-disable arrow-parens,arrow-body-style */
const getFreeSurferDataPicker = feature => freeSurferData => {
  return new FreeSurfer({ string: freeSurferData })[feature];
};
/* eslint-enable arrow-parens,arrow-body-style */

/**
 * Read file.
 *
 * @param {string} file
 * @returns {Promise} Resolves to `file`'s contents
 */
const readFileAsync = file => new Promise((resolve, reject) => {
  fs.readFile(file, (error, data) => {
    if (error) {
      reject(error);
    } else {
      resolve(data.toString());
    }
  });
});

/**
 * Get normalized tags.
 *
 * @param {Object} file
 * @param {Object} file.tags Hash map of tag names to values
 * @returns {Array} Normalized tags
 */
const getNormalizedTags = file => Object.keys(file.tags).sort().reduce(
  (memo, tag) => {
    const value = file.tags[tag];

    if (typeof value === 'boolean') {
      return memo.concat(value === true ? 1 : -1);
    } else if (typeof value === 'number') {
      return memo.concat(value);
    }

    return memo;
  },
  []
);

module.exports = {
  name: pkg.name,
  version: pkg.version,
  local: [{
    type: 'function',
    fn(opts) {
      let features = get(opts, 'remoteResult.pluginState.inputs[0][0]');
      const files = get(opts, 'userData.files');

      // TODO: This is a hack for simulator. Figure out how to load plugin state
      /* eslint-disable no-underscore-dangle */
      if (!features && opts.userData.__FEATURES__) {
        features = opts.userData.__FEATURES__;
      }
      /* eslint-enable no-underscore-dangle */

      if (!Array.isArray(features) || !features.length) {
        return Promise.reject(new Error('Expected RoI features inputs'));
      } else if (features.some(f => FreeSurfer.validFields.indexOf(f) < 0)) {
        return Promise.reject(new Error(
          `Invalid FreeSurfer feature in ${features.toString()}`
        ));
      } else if (!Array.isArray(files) || !files.length) {
        return Promise.reject(new Error('Expected user data to contain files'));
      }

      const pickFeature = getFreeSurferDataPicker(features[0]);

      return Promise.all(files.map(f => readFileAsync(f.filename)))
        .then(freeSurferDatas => ({
          X: files.map(getNormalizedTags), // covariates
          y: freeSurferDatas.map(pickFeature), // RoIs
        }));
    },
    inputs: [{
      help: 'Select Freesurfer region(s) of interest',
      label: 'Freesurfer ROI',
      type: 'select',
      values: FreeSurfer.validFields,
    }, {
      label: 'Covariates',
      type: 'covariates',
    }],
  }, {
    // step one: calculate regression result and local_meanY
    type: 'function',
    fn(opts) {
      const previousData = opts.previousData;

      for (let i = 0; i < opts.previousData.X.length; i += 1) {
        previousData.X[i].splice(0, 0, 1);
      }

      const betaVector = regression.oneShot(previousData.X, previousData.y);
      const rSquared = regression.rSquared(previousData.X,previousData.y,betaVector);
      const tValue = regression.tValue(previousData.X,previousData.y,betaVector);
      const local_meanY= n.sum(previousData.y)/previousData.y.length;
      /* eslint-disable no-console */
      console.log('X is:', previousData.X);
      console.log('y is:', previousData.y);
      console.log('beta vector is:', betaVector);
      console.log('local r square of fitting is:', rSquared);
      console.log('local tValues of betaVector are:', tValue);
      /* eslint-enable no-console */
      return { 'betaVector':betaVector,'local_meanY':local_meanY,'local_n':previousData.y.length };
      },

    }, {
     // step two: receive the global_meanY and calculate part of (y-global_meanY).^2
     type: 'function',
     fn(opts) {

        const remoteResults=opts.remoteResults;
        const global_meanY=remoteResults.global_meanY;
        console.log('global_meanY:',global_meanY);
        console.log('previousData is:',opts.previousData[0]);
        const SST_node=6765;
        return { SST_node };
     },

  }],
  remote: [{
    type: 'function',
    fn(opts) {
      const userResults = opts.userResults;

      // Not all user results contain betas. Return early.
      if (userResults.some(userResult => !((userResult || {}).data || {}).betaVector)) {
        return {};
      }

      const averageBetaVector = [];
      const betaCount = userResults[0].data.betaVector.length;
      const userCount = userResults.length;

      for (let i = 0; i < betaCount; i += 1) {
        averageBetaVector.push(userResults.reduce(
          (sum, userResult) => sum + userResult.data.betaVector[i], 0
        ) / userCount);
      }

      const global_meanY=765;
      /* eslint-disable no-console */
      console.log('Average beta vector:', averageBetaVector);
      /* eslint-enable no-console */
      console.log('opts.userResults:', userResults[0]);

      return {
        averageBetaVector,
        global_meanY,
//        complete: true,
      };
    },
    verbose: true,
  },{
    // get SST_node from each local node and calculate the statistics
     type:'function',
     fn(opts) {
     const userResults = opts.userResults;
     console.log('opts.userResults:',opts.userResults[0]);
     const SST_node1=opts.userResults[0].data.SST_node;
     return {
       SST_node1,
       complete : true,
      };
     },
     verbose: true,
   }],
  plugins: ['group-step', 'inputs'],
};
<|endoftext|>


<commit_before>/* eslint-disable filenames/match-exported */
import path from "path"
import fs from "fs-extra"
import postcss from "postcss"
import postcssImport from "postcss-import"
import postcssSass from "postcss-sass"
import postcssScss from "postcss-scss"
import postcssSmartAsset from "postcss-smart-asset"
import postcssSugarSS from "sugarss"

import { createFilter } from "rollup-pluginutils"
import { getHash } from "asset-hash"

const defaultExclude = [
  /\.json$/,
  /\.mjs$/,
  /\.js$/,
  /\.jsx$/,
  /\.es$/,
  /\.esx$/,
  /\.ts$/,
  /\.tsx$/
]

const styleParser = {
  ".pcss": null,
  ".css": null,
  ".sss": postcssSugarSS,
  ".scss": postcssScss,
  ".sass": postcssSass
}

function getPostCssPlugins(keepName) {
  return [
    postcssImport(),
    postcssSmartAsset({
      url: "copy",
      useHash: true,
      keepName
    })
  ]
}

/* eslint-disable max-params */
async function processStyle(id, fileDest, keepName) {
  const content = await fs.readFile(id)
  const parser = styleParser[path.extname(id)]
  const processor = postcss(getPostCssPlugins(keepName))
  const text = content.toString()

  const result = await processor.process(text, {
    from: id,
    to: fileDest,
    extensions: Object.keys(styleParser),
    map: { inline: false },

    // Always uses parser... even for scss as we like to offer "normal" CSS in deployed files.
    parser
  })

  await Promise.all([
    fs.outputFile(fileDest, result.css),
    fs.outputFile(`${fileDest}.map`, result.map)
  ])
}

export default function rebase(options = {}) {
  const {
    include,
    exclude = defaultExclude,
    verbose = false,
    keepName = false,
    folder: assetFolder = ""
  } = options

  const filter = createFilter(include, exclude)
  const wrappers = {}
  const assets = {}
  const files = {}

  let root = null

  function rootRelative(file) {
    return path.relative(root, file)
  }

  return {
    name: "rollup-plugin-rebase",

    /* eslint-disable complexity, max-statements */
    async resolveId(importee, importer) {
      // console.log("Resolve:", importee)
      if (!filter(importee)) {
        root = path.dirname(importee)
        return null
      }

      // Ignore root files which are typically script files. Delegate to other
      // plugins or default behavior.
      if (!importer) {
        return null
      }

      // If we process an import request of an already processed asset
      // we deal with it to exclude it from any further bundling through Rollup.
      if (assets[importee] != null) {
        return false
      }

      // We do not process files which do not have a file extensions,
      // cause all assets typically have one. By returning `null` we delegate
      // the resolver to other plugins.
      const fileExt = path.extname(importee)
      if (fileExt === "") {
        return null
      }

      const fileSource = path.resolve(path.dirname(importer), importee)
      const fileName = path.basename(importee, fileExt)
      const fileHash = await getHash(fileSource)
      const fileTarget = keepName ?
        `${fileName}~${fileHash}${fileExt}` :
        `${fileHash}${fileExt}`

      // Registering for our copying job when the bundle is created (kind of a job queue)
      // and respect any sub folder given by the configuration options.
      files[fileSource] = path.join(assetFolder, fileTarget)

      // Replacing slashes for Windows, as we need to use POSIX style to be compat
      // to Rollup imports / NodeJS resolve implementation.
      const assetId = path
        .join(root, assetFolder, fileTarget)
        .replace(/\\/g, "/")

      // console.log("Importer:", importer)
      // console.log("Asset-ID:", assetId)
      // console.log("Root-Dir:", root)
      const resolvedId = `${assetId}.js`

      // Register asset for exclusion handling in this function.
      // We need the additonal wrapping to be able to not just exclude the file
      // but to combine it with tweaking the import location. This indirection
      // makes this possible as we benefit from having a two-phase resolve.
      assets[assetId] = true

      // Store data for our dynamic wrapper generator in `load()`. This uses the
      // `assetId` to refer to our asset in its virtual new location which is
      // relative to the importer. This is enough to tweak the import statements
      // and make them flat and relative as desired in our output scenario.
      wrappers[resolvedId] = assetId

      return resolvedId
    },

    load(id) {
      if (wrappers[id] != null) {
        // It's not really any loader but kind of a dynamic code generator
        // which is just referring back to the asset we are interested in.
        // This is the magic behind the two-step-resolver and makes it possible
        // to have both: tweaked import references and externals together.
        const importee = wrappers[id]
        // console.log("Load:", importee)
        return `export { default } from "${importee}";`
      }

      return null
    },

    async generateBundle({ file, dir }) {
      const outputFolder = dir || path.dirname(file)

      try {
        // Copy all assets in parallel and waiting for it to complete.
        await Promise.all(
          Object.keys(files).map(async (fileSource) => {
            const fileDest = path.join(outputFolder, files[fileSource])
            const fileExt = path.extname(fileSource)

            // Style related files are processed, not just copied, so that
            // we can handle internal references in CSS as well.
            if (fileExt in styleParser) {
              if (verbose) {
                console.log(`Processing ${rootRelative(fileSource)} => ${rootRelative(fileDest)}...`)
              }

              await processStyle(fileSource, fileDest, keepName)
            } else {
              if (verbose) {
                console.log(`Copying ${rootRelative(fileSource)} => ${rootRelative(fileDest)}...`)
              }

              await fs.copy(fileSource, fileDest)
            }
          })
        )
      } catch (error) {
        throw new Error("Error while copying files:", error)
      }
    }
  }
}
<commit_msg>Fixed issues with script files with extensions. Looked like the previous behavior to filter extensions was broken overall. Also fixes root detection.<commit_after>/* eslint-disable filenames/match-exported */
import path from "path"
import fs from "fs-extra"
import postcss from "postcss"
import postcssImport from "postcss-import"
import postcssSass from "postcss-sass"
import postcssScss from "postcss-scss"
import postcssSmartAsset from "postcss-smart-asset"
import postcssSugarSS from "sugarss"

import { createFilter } from "rollup-pluginutils"
import { getHash } from "asset-hash"

const scriptExtensions = /^\.(json|mjs|js|jsx|es|esx|ts|tsx)$/

const styleParser = {
  ".pcss": null,
  ".css": null,
  ".sss": postcssSugarSS,
  ".scss": postcssScss,
  ".sass": postcssSass
}

function getPostCssPlugins(keepName) {
  return [
    postcssImport(),
    postcssSmartAsset({
      url: "copy",
      useHash: true,
      keepName
    })
  ]
}

/* eslint-disable max-params */
async function processStyle(id, fileDest, keepName) {
  const content = await fs.readFile(id)
  const parser = styleParser[path.extname(id)]
  const processor = postcss(getPostCssPlugins(keepName))
  const text = content.toString()

  const result = await processor.process(text, {
    from: id,
    to: fileDest,
    extensions: Object.keys(styleParser),
    map: { inline: false },

    // Always uses parser... even for scss as we like to offer "normal" CSS in deployed files.
    parser
  })

  await Promise.all([
    fs.outputFile(fileDest, result.css),
    fs.outputFile(`${fileDest}.map`, result.map)
  ])
}

export default function rebase(options = {}) {
  const {
    include,
    exclude,
    verbose = false,
    keepName = false,
    folder: assetFolder = ""
  } = options

  const filter = createFilter(include, exclude)
  const wrappers = {}
  const assets = {}
  const files = {}

  let root = null

  function rootRelative(file) {
    return path.relative(root, file)
  }

  return {
    name: "rollup-plugin-rebase",

    /* eslint-disable complexity, max-statements */
    async resolveId(importee, importer) {
      if (!filter(importee)) {
        return null
      }

      // Ignore root files which are typically script files. Delegate to other
      // plugins or default behavior.
      if (!importer) {
        root = path.dirname(importee)
        return null
      }

      // If we process an import request of an already processed asset
      // we deal with it to exclude it from any further bundling through Rollup.
      if (assets[importee] != null) {
        return false
      }

      // We do not process files which do not have a file extensions,
      // cause all assets typically have one. By returning `null` we delegate
      // the resolver to other plugins.
      const fileExt = path.extname(importee)
      if (fileExt === "" || scriptExtensions.test(fileExt)) {
        return null
      }

      const fileSource = path.resolve(path.dirname(importer), importee)
      const fileName = path.basename(importee, fileExt)
      const fileHash = await getHash(fileSource)
      const fileTarget = keepName ?
        `${fileName}~${fileHash}${fileExt}` :
        `${fileHash}${fileExt}`

      // Registering for our copying job when the bundle is created (kind of a job queue)
      // and respect any sub folder given by the configuration options.
      files[fileSource] = path.join(assetFolder, fileTarget)

      // Replacing slashes for Windows, as we need to use POSIX style to be compat
      // to Rollup imports / NodeJS resolve implementation.
      const assetId = path
        .join(root, assetFolder, fileTarget)
        .replace(/\\/g, "/")

      // console.log("Importer:", importer)
      // console.log("Asset-ID:", assetId)
      // console.log("Root-Dir:", root)
      const resolvedId = `${assetId}.js`

      // Register asset for exclusion handling in this function.
      // We need the additonal wrapping to be able to not just exclude the file
      // but to combine it with tweaking the import location. This indirection
      // makes this possible as we benefit from having a two-phase resolve.
      assets[assetId] = true

      // Store data for our dynamic wrapper generator in `load()`. This uses the
      // `assetId` to refer to our asset in its virtual new location which is
      // relative to the importer. This is enough to tweak the import statements
      // and make them flat and relative as desired in our output scenario.
      wrappers[resolvedId] = assetId

      return resolvedId
    },

    load(id) {
      if (wrappers[id] != null) {
        // It's not really any loader but kind of a dynamic code generator
        // which is just referring back to the asset we are interested in.
        // This is the magic behind the two-step-resolver and makes it possible
        // to have both: tweaked import references and externals together.
        const importee = wrappers[id]
        // console.log("Load:", importee)
        return `export { default } from "${importee}";`
      }

      return null
    },

    async generateBundle({ file, dir }) {
      const outputFolder = dir || path.dirname(file)

      try {
        // Copy all assets in parallel and waiting for it to complete.
        await Promise.all(
          Object.keys(files).map(async (fileSource) => {
            const fileDest = path.join(outputFolder, files[fileSource])
            const fileExt = path.extname(fileSource)

            // Style related files are processed, not just copied, so that
            // we can handle internal references in CSS as well.
            if (fileExt in styleParser) {
              if (verbose) {
                console.log(`Processing ${rootRelative(fileSource)} => ${rootRelative(fileDest)}...`)
              }

              await processStyle(fileSource, fileDest, keepName)
            } else {
              if (verbose) {
                console.log(`Copying ${rootRelative(fileSource)} => ${rootRelative(fileDest)}...`)
              }

              await fs.copy(fileSource, fileDest)
            }
          })
        )
      } catch (error) {
        throw new Error("Error while copying files:", error)
      }
    }
  }
}
<|endoftext|>


<commit_before>'use strict'

const browserify = require('./browserify')
const uglifyjs   = require('./uglifyjs')

/**
 * Load, transform, bundle and compress JS.
 * @public
 * @param {String} filePath - Absolute path to file.
 * @param {?Object} opts - Options.
 * @returns {Promise} Returns the following properties if resolved: {String}.
 */
module.exports = function(filePath, opts) {

	return Promise.resolve().then(() => {

		if (typeof filePath!=='string')           throw new Error(`'filePath' must be a string`)
		if (typeof opts!=='object' && opts!=null) throw new Error(`'opts' must be undefined, null or an object`)

	}).then((str) => {

		// Process data with browserify
		return browserify(filePath, opts)

	}).then((str) => {

		// Process data with uglifyjs
		return uglifyjs(str, opts)

	}).then((str) => {

		// Return data
		return str

	})

}

/**
 * Tell Rosid with which file extension it should load the file.
 * @public
 * @param {?Object} opts - Options.
 * @returns {String} File extension.
 */
module.exports.in = function(opts) {

	return (opts!=null && opts.in!=null) ? opts.in : '.js'

}

/**
 * Tell Rosid with which file extension it should save the file.
 * @public
 * @param {?Object} opts - Options.
 * @returns {String} File extension.
 */
module.exports.out = function(opts) {

	return (opts!=null && opts.out!=null) ? opts.out : '.js'

}

/**
 * Attach an array to the function, which contains a list of
 * extensions used by the handler. The array will be used by Rosid for caching purposes.
 * @public
 */
module.exports.cache = [
	'.js',
	'.json'
]<commit_msg>Support for next version of Rosid<commit_after>'use strict'

const browserify = require('./browserify')
const uglifyjs   = require('./uglifyjs')

/**
 * Load, transform, bundle and compress JS.
 * @public
 * @param {String} filePath - Absolute path to file.
 * @param {?Object} opts - Options.
 * @returns {Promise} Returns the following properties if resolved: {String}.
 */
module.exports = function(filePath, opts) {

	return Promise.resolve().then(() => {

		if (typeof filePath!=='string')           throw new Error(`'filePath' must be a string`)
		if (typeof opts!=='object' && opts!=null) throw new Error(`'opts' must be undefined, null or an object`)

	}).then((str) => {

		// Process data with browserify
		return browserify(filePath, opts)

	}).then((str) => {

		// Process data with uglifyjs
		return uglifyjs(str, opts)

	}).then((str) => {

		// Return data
		return str

	})

}

/**
 * Tell Rosid with which file extension it should load the file.
 * @public
 * @param {?Object} opts - Options.
 * @returns {String} File extension.
 */
module.exports.in = function(opts) {

	return (opts!=null && opts.in!=null) ? opts.in : '.js'

}

/**
 * Tell Rosid with which file extension it should save the file.
 * @public
 * @param {?Object} opts - Options.
 * @returns {String} File extension.
 */
module.exports.out = function(opts) {

	return (opts!=null && opts.out!=null) ? opts.out : '.js'

}

/**
 * Attach an array to the function, which contains a list of
 * file patterns used by the handler. The array will be used by Rosid for caching purposes.
 * @public
 */
module.exports.cache = [
	'**/*.js',
	'**/*.json'
]<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'

export default class PieChart {
  static propTypes = {
    data: PropTypes.arrayOf(PropTypes.shape({
      value: PropTypes.number,
      color: PropTypes.string,
    })),
    palette: PropTypes.arrayOf(PropTypes.string),
    sectorStrokeWidth: PropTypes.number,
    expandOnHover: PropTypes.bool,
    expandedSector: PropTypes.number,
    onSectorHover: PropTypes.func,
    expandPx: PropTypes.number,
    viewBoxWidth: PropTypes.number,
  }

  static defaultProps = {
    data: [],
    palette: [
      '#2ecc71',
      '#3498db',
      '#9b59b6',
      '#f1c40f',
      '#e67e22',
      '#e74c3c',
      '#1abc9c',
    ],
    sectorStrokeWidth: 3,
    expandOnHover: true,
    expandedSector: null,
    expandPx: 10,
    viewBoxWidth: 300,
  }

  handleMouseEnterOnSector(i) {
    const { onMouseEnterOnSector } = this.props

    if (onMouseEnterOnSector) {
      onMouseEnterOnSector(i)
    }
  }

  handleMouseLeaveFromSector() {
    const { onMouseLeaveFromSector } = this.props

    if (onMouseLeaveFromSector) {
      onMouseLeaveFromSector(null)
    }
  }

  getSectors() {
    const { data, palette, sectorStrokeWidth, expandOnHover, expandedSector, expandPx, viewBoxWidth } = this.props
    const total = Math.ceil(data.reduce((n, d) => d.value + n, 0))
    const center = viewBoxWidth / 2
    let startAngle = 0
    let endAngle = 0

    return (
      data.map((d, i) => {
        const expandVal = expandOnHover && expandedSector === i ? expandPx : 0
        const angle = 360 * d.value / total

        startAngle = endAngle
        endAngle = startAngle + angle

        const x1 = Math.round(center + (center + expandVal) * Math.cos(Math.PI * startAngle / 180))
        const y1 = Math.round(center + (center + expandVal) * Math.sin(Math.PI * startAngle / 180))

        const x2 = Math.round(center + (center + expandVal) * Math.cos(Math.PI * endAngle / 180))
        const y2 = Math.round(center + (center + expandVal) * Math.sin(Math.PI * endAngle / 180))

        const dPath =
          'M' + center + ',' + center + ' ' +
          'L' + x1 + ',' + y1 + ' ' +
          'A' + (center + expandVal) + ',' + (center + expandVal) + ' 0 0,1 ' + x2 + ',' + y2 + ' ' +
          'z'

        return (
          <path
            key={ 'sector' + i }
            d={ dPath }
            fill={ d.color || palette[i % palette.length] }
            stroke="#fff"
            strokeWidth={ sectorStrokeWidth }
            onMouseEnter={ () => this.handleMouseEnterOnSector(i) }
            onMouseLeave={ () => this.handleMouseLeaveFromSector() } />
        )
      })
    )
  }

  render() {
    const { expandPx, viewBoxWidth, props } = this.props
    return (
      <svg viewBox={ `0 0 ${viewBoxWidth + expandPx * 2} ${viewBoxWidth + expandPx * 2}` } { ...props }>
        <g transform={`translate(${expandPx}, ${expandPx})`}>
          { this.getSectors() }
        </g>
      </svg>
  )}
}
<commit_msg>fix(getSectors): draw long arc if sector > 50%<commit_after>import React, { PropTypes } from 'react'

export default class PieChart {
  static propTypes = {
    data: PropTypes.arrayOf(PropTypes.shape({
      value: PropTypes.number,
      color: PropTypes.string,
    })),
    palette: PropTypes.arrayOf(PropTypes.string),
    sectorStrokeWidth: PropTypes.number,
    expandOnHover: PropTypes.bool,
    expandedSector: PropTypes.number,
    onSectorHover: PropTypes.func,
    expandPx: PropTypes.number,
    viewBoxWidth: PropTypes.number,
  }

  static defaultProps = {
    data: [],
    palette: [
      '#2ecc71',
      '#3498db',
      '#9b59b6',
      '#f1c40f',
      '#e67e22',
      '#e74c3c',
      '#1abc9c',
    ],
    sectorStrokeWidth: 3,
    expandOnHover: true,
    expandedSector: null,
    expandPx: 10,
    viewBoxWidth: 300,
  }

  handleMouseEnterOnSector(i) {
    const { onMouseEnterOnSector } = this.props

    if (onMouseEnterOnSector) {
      onMouseEnterOnSector(i)
    }
  }

  handleMouseLeaveFromSector() {
    const { onMouseLeaveFromSector } = this.props

    if (onMouseLeaveFromSector) {
      onMouseLeaveFromSector(null)
    }
  }

  getSectors() {
    const { data, palette, sectorStrokeWidth, expandOnHover, expandedSector, expandPx, viewBoxWidth } = this.props
    const total = Math.ceil(data.reduce((n, d) => d.value + n, 0))
    const center = viewBoxWidth / 2
    let startAngle = 0
    let endAngle = 0

    return (
      data.map((d, i) => {
        const expandVal = expandOnHover && expandedSector === i ? expandPx : 0
        const angle = 360 * d.value / total
        const largeArc = (d.value / total) <= 0.5 ? 0 : 1

        startAngle = endAngle
        endAngle = startAngle + angle

        const x1 = Math.round(center + (center + expandVal) * Math.cos(Math.PI * startAngle / 180))
        const y1 = Math.round(center + (center + expandVal) * Math.sin(Math.PI * startAngle / 180))

        const x2 = Math.round(center + (center + expandVal) * Math.cos(Math.PI * endAngle / 180))
        const y2 = Math.round(center + (center + expandVal) * Math.sin(Math.PI * endAngle / 180))

        const dPath =
          'M' + center + ',' + center + ' ' +
          'L' + x1 + ',' + y1 + ' ' +
          'A' + (center + expandVal) + ',' + (center + expandVal) + ' 0 ' + largeArc + ',1 ' + x2 + ',' + y2 + ' ' +
          'z'

        return (
          <path
            key={ 'sector' + i }
            d={ dPath }
            fill={ d.color || palette[i % palette.length] }
            stroke="#fff"
            strokeWidth={ sectorStrokeWidth }
            onMouseEnter={ () => this.handleMouseEnterOnSector(i) }
            onMouseLeave={ () => this.handleMouseLeaveFromSector() } />
        )
      })
    )
  }

  render() {
    const { expandPx, viewBoxWidth, props } = this.props
    return (
      <svg viewBox={ `0 0 ${viewBoxWidth + expandPx * 2} ${viewBoxWidth + expandPx * 2}` } { ...props }>
        <g transform={`translate(${expandPx}, ${expandPx})`}>
          { this.getSectors() }
        </g>
      </svg>
  )}
}
<|endoftext|>


<commit_before>/* @license minigrid v1.6.5 - minimal cascading grid layout http://alves.im/minigrid */
(function (exports) {

  'use strict';

  var transformProp;
  var loaded;
  (function () {
    var style = document.createElement('a').style;
    var prop;
    if (style[prop = 'webkitTransform'] !== undefined) {
      transformProp = prop;
    }
    if (style[prop = 'msTransform'] !== undefined) {
      transformProp = prop;
    }
    if (style[prop = 'transform'] !== undefined) {
      transformProp = prop;
    }
  }());

  function forEach (arr, cb) {
    if (arr) {
      for (var i = 0, len = arr.length; i < len; i++) {
        if (arr[i]) {
          cb(arr[i], i, arr);
        }
      }
    }
  }

  function minigrid (props) {
    if (loaded) {
      init(props);
      return;
    }
    window.onload = function() {
      init(props);
    }
  }

  function init(props) {
    var containerEle = props.container instanceof Node ?
      props.container : document.querySelector(props.container);
    if (!containerEle) { return false; }
    containerEle.classList.add(containerEle.className.split(' ')[0] + '--loaded');
    loaded = true;
    var itemsNodeList = containerEle.querySelectorAll(props.item);
    if (itemsNodeList.length === 0) { return false; }
    var gutter = (
      typeof props.gutter === 'number' &&
      isFinite(props.gutter) &&
      Math.floor(props.gutter) === props.gutter
    ) ? props.gutter : 6;
    containerEle.style.width = '';
    var containerWidth = containerEle.getBoundingClientRect().width;
    var firstChildWidth = itemsNodeList[0].getBoundingClientRect().width + gutter;
    var cols = Math.max(Math.floor((containerWidth - gutter) / firstChildWidth), 1);
    var count = 0;
    containerWidth = (firstChildWidth * cols + gutter) + 'px';
    containerEle.style.width = containerWidth;
    containerEle.style.position = 'relative';

    var itemsGutter = [];
    var itemsPosX = [];
    for ( var g = 0 ; g < cols ; ++g ) {
      itemsPosX.push(g * firstChildWidth + gutter);
      itemsGutter.push(gutter);
    }

    forEach(itemsNodeList, function (item) {
      var itemIndex = itemsGutter
        .slice(0)
        .sort(function (a, b) {
          return a - b;
        })
        .shift();
      itemIndex = itemsGutter.indexOf(itemIndex);
      var posX = itemsPosX[itemIndex];
      var posY = itemsGutter[itemIndex];
      item.style.position = 'absolute';
      item.classList.add(item.className.split(' ')[0] + '--animate');
      if (!props.animate && transformProp) {
        item.style[transformProp] = 'translate3D(' + posX + 'px,' + posY + 'px, 0)';
      }
      itemsGutter[itemIndex] += item.getBoundingClientRect().height + gutter;
      count = count + 1;
      if (props.animate) {
        return props.animate(item, posX, posY, count);
      }
    });

    var containerHeight = itemsGutter
      .slice(0)
      .sort(function (a, b) {
        return a - b;
      })
      .pop();

    containerEle.style.height = containerHeight + 'px';

    if (typeof props.done === 'function') {
      props.done(itemsNodeList);
    }
  }

  if (typeof define === 'function' && define.amd) {
    define(function() { return minigrid; });
  } else if (typeof module !== 'undefined' && module.exports) {
    module.exports = minigrid;
  } else {
    exports.minigrid = minigrid;
  }

})(this);
<commit_msg>Cleanup code<commit_after>(function (exports) {

  'use strict';

  var transformProp;
  var loaded;
  (function () {
    var style = document.createElement('a').style;
    var prop;
    if (style[prop = 'webkitTransform'] !== undefined) {
      transformProp = prop;
    }
    if (style[prop = 'msTransform'] !== undefined) {
      transformProp = prop;
    }
    if (style[prop = 'transform'] !== undefined) {
      transformProp = prop;
    }
  }());

  function forEach (arr, cb) {
    if (arr) {
      for (var i = 0, len = arr.length; i < len; i++) {
        if (arr[i]) {
          cb(arr[i], i, arr);
        }
      }
    }
  }

  function minigrid (props) {
    if (loaded) {
      init(props);
      return;
    }
    window.onload = function() {
      init(props);
    }
  }

  function init(props) {
    var containerEle = props.container instanceof Node ?
      props.container : document.querySelector(props.container);

    if (!containerEle) {
      return false;
    }

    containerEle.classList.add(containerEle.className.split(' ')[0] + '--loaded');
    loaded = true;

    var itemsNodeList = containerEle.querySelectorAll(props.item);
    if (itemsNodeList.length === 0) {
      return false;
    }

    var gutter = (
      typeof props.gutter === 'number' &&
      isFinite(props.gutter) &&
      Math.floor(props.gutter) === props.gutter
    ) ? props.gutter : 6;
    containerEle.style.width = '';

    var containerWidth = containerEle.getBoundingClientRect().width;
    var firstChildWidth = itemsNodeList[0].getBoundingClientRect().width + gutter;
    var cols = Math.max(Math.floor((containerWidth - gutter) / firstChildWidth), 1);
    var count = 0;

    containerWidth = (firstChildWidth * cols + gutter) + 'px';
    containerEle.style.width = containerWidth;
    containerEle.style.position = 'relative';

    var itemsGutter = [];
    var itemsPosX = [];

    for ( var g = 0 ; g < cols ; ++g ) {
      itemsPosX.push(g * firstChildWidth + gutter);
      itemsGutter.push(gutter);
    }

    forEach(itemsNodeList, function (item) {
      var itemIndex = itemsGutter
        .slice(0)
        .sort(function (a, b) {
          return a - b;
        })
        .shift();
      itemIndex = itemsGutter.indexOf(itemIndex);

      var posX = itemsPosX[itemIndex];
      var posY = itemsGutter[itemIndex];

      item.style.position = 'absolute';
      item.classList.add(item.className.split(' ')[0] + '--animate');

      if (!props.animate && transformProp) {
        item.style[transformProp] = 'translate3D(' + posX + 'px,' + posY + 'px, 0)';
      }

      itemsGutter[itemIndex] += item.getBoundingClientRect().height + gutter;
      count = count + 1;

      if (props.animate) {
        return props.animate(item, posX, posY, count);
      }

    });

    var containerHeight = itemsGutter
      .slice(0)
      .sort(function (a, b) {
        return a - b;
      })
      .pop();

    containerEle.style.height = containerHeight + 'px';

    if (typeof props.done === 'function') {
      props.done(itemsNodeList);
    }
  }

  if (typeof define === 'function' && define.amd) {
    define(function() { return minigrid; });
  } else if (typeof module !== 'undefined' && module.exports) {
    module.exports = minigrid;
  } else {
    exports.minigrid = minigrid;
  }

})(this);
<|endoftext|>


<commit_before>import moment from 'moment';
import { getFilteredEvents } from './util/event-utils';
import { plotEvents, setMapPositionBasedOnZip } from './util/map-utils';
import { getHash, setHash, onHashChange } from './util/url-hash';
import { pollForNewEvents } from './util/events-poll';

// temporary thing so that changing zipcode via UI updates query
document.getElementById('zipcode').addEventListener('input', (event) => {
  const value = event.target.value;
  if (/^\d+$/.test(value) && value.length === 5) {
    setHash({ zipcode: value });
  }
});

// temporary thing for setting event type and start/end dates via JS
// console, e.g. setHash({ eventType: 'hi', startDate: '2017-03-21'})
window.setHash = setHash;

// Boot:

// FIXME this stuff will be split into A) the map component and B)
// top level app state stuff which the event map and event list
// are a function of.
let eventsLayer = null;

function plotAndZoom(filters) {

  // wipe out existing plotted events -- probably this can
  // be done more efficiently
  if (eventsLayer) {
    eventsLayer.clearLayers();
  }

  // compute filtered event set
  const filteredEvents = getFilteredEvents(window.PEOPLEPOWER_EVENTS, filters);

  // then plot those events
  eventsLayer = plotEvents(filteredEvents, window.map);

  // then adjust map position accordingly
  setMapPositionBasedOnZip(
    filters.zipcode,
    window.KNOWN_ZIPCODES,
    filteredEvents,
    window.map
  );
}

// Once initially on boot, based on initial filters in URL
plotAndZoom(getHash());

// Also when the event filters change
onHashChange(plotAndZoom);

// Now poll for new events data and when it arrives, update data
pollForNewEvents(5000, (err) => {
  if (!err) {
    console.log('got new data!');
    // window.PEOPLEPOWER_EVENTS has just been updated, so update the
    // app state accordingly and let the UI react to that.
  }
});

// Start up Vue
import Vue from 'vue';

//Just a quick dummy app to make sure vue is working
let app = new Vue({
  el: '#clock',
  template: '
The current time is: {{time}}
',
  data: {
    time: moment().format('hh:mm:ss a')
  },
  style: {
    fontSize: '20px'
  }
})

function tick(){
  setTimeout(function(){
    app.$data.time = moment().format('hh:mm:ss a');
    tick();
  },1000);
};tick();
<commit_msg>Longer poll duration<commit_after>import moment from 'moment';
import { getFilteredEvents } from './util/event-utils';
import { plotEvents, setMapPositionBasedOnZip } from './util/map-utils';
import { getHash, setHash, onHashChange } from './util/url-hash';
import { pollForNewEvents } from './util/events-poll';

// temporary thing so that changing zipcode via UI updates query
document.getElementById('zipcode').addEventListener('input', (event) => {
  const value = event.target.value;
  if (/^\d+$/.test(value) && value.length === 5) {
    setHash({ zipcode: value });
  }
});

// temporary thing for setting event type and start/end dates via JS
// console, e.g. setHash({ eventType: 'hi', startDate: '2017-03-21'})
window.setHash = setHash;

// Boot:

// FIXME this stuff will be split into A) the map component and B)
// top level app state stuff which the event map and event list
// are a function of.
let eventsLayer = null;

function plotAndZoom(filters) {

  // wipe out existing plotted events -- probably this can
  // be done more efficiently
  if (eventsLayer) {
    eventsLayer.clearLayers();
  }

  // compute filtered event set
  const filteredEvents = getFilteredEvents(window.PEOPLEPOWER_EVENTS, filters);

  // then plot those events
  eventsLayer = plotEvents(filteredEvents, window.map);

  // then adjust map position accordingly
  setMapPositionBasedOnZip(
    filters.zipcode,
    window.KNOWN_ZIPCODES,
    filteredEvents,
    window.map
  );
}

// Once initially on boot, based on initial filters in URL
plotAndZoom(getHash());

// Also when the event filters change
onHashChange(plotAndZoom);

// Now poll for new events data and when it arrives, update data
pollForNewEvents(30000, (err) => {
  if (!err) {
    console.log('got new data!');
    // window.PEOPLEPOWER_EVENTS has just been updated, so update the
    // app state accordingly and let the UI react to that.
  }
});

// Start up Vue
import Vue from 'vue';

//Just a quick dummy app to make sure vue is working
let app = new Vue({
  el: '#clock',
  template: '
The current time is: {{time}}
',
  data: {
    time: moment().format('hh:mm:ss a')
  },
  style: {
    fontSize: '20px'
  }
})

function tick(){
  setTimeout(function(){
    app.$data.time = moment().format('hh:mm:ss a');
    tick();
  },1000);
};tick();
<|endoftext|>


<commit_before>import JavaFormatter from "./javaFormatter"

export default Formatter = {
    createJavaFormatter(spacing) {
        return new JavaFormatter(spacing)
    },

    testModule() {
        console.log("I am working")
    }
}
<commit_msg>trying more new things<commit_after>import JavaFormatter from "./javaFormatter"

const Formatter = {
    createJavaFormatter(spacing) {
        return new JavaFormatter(spacing)
    },

    testModule() {
        console.log("I am working")
    }
}

export default Formatter
<|endoftext|>