<commit_before>import debug from 'debug';
import moment from 'moment';
import { sortBy, uniqBy, values, some, each, isArray, isNumber, isString, includes, forOwn } from 'lodash';

const logger = debug('redash:services:QueryResult');
const filterTypes = ['filter', 'multi-filter', 'multiFilter'];

const ALL_VALUES = '*';
const NONE_VALUES = '-';

function getColumnNameWithoutType(column) {
  let typeSplit;
  if (column.indexOf('::') !== -1) {
    typeSplit = '::';
  } else if (column.indexOf('__') !== -1) {
    typeSplit = '__';
  } else {
    return column;
  }

  const parts = column.split(typeSplit);
  if (parts[0] === '' && parts.length === 2) {
    return parts[1];
  }

  if (!includes(filterTypes, parts[1])) {
    return column;
  }

  return parts[0];
}

export function getColumnCleanName(column) {
  return getColumnNameWithoutType(column);
}

function getColumnFriendlyName(column) {
  return getColumnNameWithoutType(column).replace(/(?:^|\s)\S/g, a =>
    a.toUpperCase());
}

function addPointToSeries(point, seriesCollection, seriesName) {
  if (seriesCollection[seriesName] === undefined) {
    seriesCollection[seriesName] = {
      name: seriesName,
      type: 'column',
      data: [],
    };
  }

  seriesCollection[seriesName].data.push(point);
}

function QueryResultService($resource, $timeout, $q, QueryResultError) {
  const QueryResultResource = $resource('api/query_results/:id', { id: '@id' }, { post: { method: 'POST' } });
  const Job = $resource('api/jobs/:id', { id: '@id' });
  const statuses = {
    1: 'waiting',
    2: 'processing',
    3: 'done',
    4: 'failed',
  };

  function handleErrorResponse(queryResult, response) {
    if (response.status === 403) {
      queryResult.update(response.data);
    } else if (response.status === 400 && 'job' in response.data) {
      queryResult.update(response.data);
    } else {
      logger('Unknown error', response);
      queryResult.update({
        job: {
          error: 'unknown error occurred. Please try again later.',
          status: 4,
        },
      });
    }
  }

  class QueryResult {
    constructor(props) {
      this.deferred = $q.defer();
      this.job = {};
      this.query_result = {};
      this.status = 'waiting';
      this.filters = undefined;
      this.filterFreeze = undefined;

      this.updatedAt = moment();

      // extended status flags
      this.isLoadingResult = false;

      if (props) {
        this.update(props);
      }
    }

    update(props) {
      Object.assign(this, props);

      if ('query_result' in props) {
        this.status = 'done';
        this.filters = undefined;
        this.filterFreeze = undefined;

        const columnTypes = {};

        // TODO: we should stop manipulating incoming data, and switch to relaying
        // on the column type set by the backend. This logic is prone to errors,
        // and better be removed. Kept for now, for backward compatability.
        each(this.query_result.data.rows, (row) => {
          forOwn(row, (v, k) => {
            let newType = null;
            if (isNumber(v)) {
              newType = 'float';
            } else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}T/)) {
              row[k] = moment.utc(v);
              newType = 'datetime';
            } else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}$/)) {
              row[k] = moment.utc(v);
              newType = 'date';
            } else if (typeof (v) === 'object' && v !== null) {
              row[k] = JSON.stringify(v);
            } else {
              newType = 'string';
            }

            if (newType !== null) {
              if (columnTypes[k] !== undefined && columnTypes[k] !== newType) {
                columnTypes[k] = 'string';
              } else {
                columnTypes[k] = newType;
              }
            }
          });
        });

        each(this.query_result.data.columns, (column) => {
          column.name = '' + column.name;
          if (columnTypes[column.name]) {
            if (column.type == null || column.type === 'string') {
              column.type = columnTypes[column.name];
            }
          }
        });

        this.deferred.resolve(this);
      } else if (this.job.status === 3) {
        this.status = 'processing';
      } else if (this.job.status === 4) {
        this.status = statuses[this.job.status];
        this.deferred.reject(new QueryResultError(this.job.error));
      } else {
        this.status = undefined;
      }
    }

    getId() {
      let id = null;
      if ('query_result' in this) {
        id = this.query_result.id;
      }
      return id;
    }

    cancelExecution() {
      Job.delete({ id: this.job.id });
    }

    getStatus() {
      if (this.isLoadingResult) {
        return 'loading-result';
      }
      return this.status || statuses[this.job.status];
    }

    getError() {
      // TODO: move this logic to the server...
      if (this.job.error === 'None') {
        return undefined;
      }

      return this.job.error;
    }

    getLog() {
      if (!this.query_result.data ||
          !this.query_result.data.log ||
          this.query_result.data.log.length === 0) {
        return null;
      }

      return this.query_result.data.log;
    }

    getUpdatedAt() {
      return this.query_result.retrieved_at || this.job.updated_at * 1000.0 || this.updatedAt;
    }

    getRuntime() {
      return this.query_result.runtime;
    }

    getRawData() {
      if (!this.query_result.data) {
        return null;
      }

      return this.query_result.data.rows;
    }

    getData() {
      if (!this.query_result.data) {
        return null;
      }

      function filterValues(filters) {
        if (!filters) {
          return null;
        }

        return filters.reduce(
          (str, filter) =>
            str + filter.current
          , '',
        );
      }

      const filters = this.getFilters();
      const filterFreeze = filterValues(filters);

      if (this.filterFreeze !== filterFreeze) {
        this.filterFreeze = filterFreeze;

        if (filters) {
          filters.forEach((filter) => {
            if (filter.multiple && includes(filter.current, ALL_VALUES)) {
              filter.current = filter.values.slice(2);
            }

            if (filter.multiple && includes(filter.current, NONE_VALUES)) {
              filter.current = [];
            }
          });

          this.filteredData = this.query_result.data.rows.filter(row =>
            filters.reduce((memo, filter) => {
              if (!isArray(filter.current)) {
                filter.current = [filter.current];
              }

              return (memo && some(filter.current, (v) => {
                const value = row[filter.name];
                if (moment.isMoment(value)) {
                  return value.isSame(v);
                }
                // We compare with either the value or the String representation of the value,
                // because Select2 casts true/false to "true"/"false".
                return (v === value || String(value) === v);
              }));
            }, true));
        } else {
          this.filteredData = this.query_result.data.rows;
        }
      }

      return this.filteredData;
    }

    isEmpty() {
      return this.getData() === null || this.getData().length === 0;
    }

    getChartData(mapping) {
      const series = {};

      this.getData().forEach((row) => {
        let point = { $raw: row };
        let seriesName;
        let xValue = 0;
        const yValues = {};
        let eValue = null;
        let sizeValue = null;
        let zValue = null;

        forOwn(row, (v, definition) => {
          definition = '' + definition;
          const definitionParts = definition.split('::') || definition.split('__');
          const name = definitionParts[0];
          const type = mapping ? mapping[definition] : definitionParts[1];
          let value = v;

          if (type === 'unused') {
            return;
          }

          if (type === 'x') {
            xValue = value;
            point[type] = value;
          }
          if (type === 'y') {
            if (value == null) {
              value = 0;
            }
            yValues[name] = value;
            point[type] = value;
          }
          if (type === 'yError') {
            eValue = value;
            point[type] = value;
          }

          if (type === 'series') {
            seriesName = String(value);
          }

          if (type === 'size') {
            point[type] = value;
            sizeValue = value;
          }

          if (type === 'zVal') {
            point[type] = value;
            zValue = value;
          }

          if (type === 'multiFilter' || type === 'multi-filter') {
            seriesName = String(value);
          }
        });

        if (seriesName === undefined) {
          each(yValues, (yValue, ySeriesName) => {
            point = { x: xValue, y: yValue, $raw: point.$raw };
            if (eValue !== null) {
              point.yError = eValue;
            }

            if (sizeValue !== null) {
              point.size = sizeValue;
            }

            if (zValue !== null) {
              point.zVal = zValue;
            }
            addPointToSeries(point, series, ySeriesName);
          });
        } else {
          addPointToSeries(point, series, seriesName);
        }
      });
      return sortBy(values(series), 'name');
    }

    getColumns() {
      if (this.columns === undefined && this.query_result.data) {
        this.columns = this.query_result.data.columns;
      }

      return this.columns;
    }

    getColumnNames() {
      if (this.columnNames === undefined && this.query_result.data) {
        this.columnNames = this.query_result.data.columns.map(v => v.name);
      }

      return this.columnNames;
    }

    getColumnCleanNames() {
      return this.getColumnNames().map(col => getColumnCleanName(col));
    }

    getColumnFriendlyNames() {
      return this.getColumnNames().map(col => getColumnFriendlyName(col));
    }

    getFilters() {
      if (!this.filters) {
        this.prepareFilters();
      }

      return this.filters;
    }

    prepareFilters() {
      if (!this.getColumns()) {
        return;
      }

      const filters = [];

      this.getColumns().forEach((col) => {
        const name = col.name;
        const type = name.split('::')[1] || name.split('__')[1];
        if (includes(filterTypes, type)) {
          // filter found
          const filter = {
            name,
            friendlyName: getColumnFriendlyName(name),
            column: col,
            values: [],
            multiple: (type === 'multiFilter') || (type === 'multi-filter'),
          };
          filters.push(filter);
        }
      }, this);

      this.getRawData().forEach((row) => {
        filters.forEach((filter) => {
          filter.values.push(row[filter.name]);
          if (filter.values.length === 1) {
            if (filter.multiple) {
              filter.current = [row[filter.name]];
            } else {
              filter.current = row[filter.name];
            }
          }
        });
      });

      filters.forEach((filter) => {
        if (filter.multiple) {
          filter.values.unshift(ALL_VALUES);
          filter.values.unshift(NONE_VALUES);
        }
      });

      filters.forEach((filter) => {
        filter.values = uniqBy(filter.values, (v) => {
          if (moment.isMoment(v)) {
            return v.unix();
          }
          return v;
        });
      });

      this.filters = filters;
    }

    toPromise() {
      return this.deferred.promise;
    }

    static getById(id) {
      const queryResult = new QueryResult();

      queryResult.isLoadingResult = true;
      QueryResultResource.get({ id }, (response) => {
        // Success handler
        queryResult.isLoadingResult = false;
        queryResult.update(response);
      }, (error) => {
        // Error handler
        queryResult.isLoadingResult = false;
        handleErrorResponse(queryResult, error);
      });

      return queryResult;
    }

    loadResult(tryCount) {
      this.isLoadingResult = true;
      QueryResultResource.get(
        { id: this.job.query_result_id },
        (response) => {
          this.update(response);
          this.isLoadingResult = false;
        },
        (error) => {
          if (tryCount === undefined) {
            tryCount = 0;
          }

          if (tryCount > 3) {
            logger('Connection error while trying to load result', error);
            this.update({
              job: {
                error: 'failed communicating with server. Please check your Internet connection and try again.',
                status: 4,
              },
            });
            this.isLoadingResult = false;
          } else {
            $timeout(() => {
              this.loadResult(tryCount + 1);
            }, 1000 * Math.pow(2, tryCount));
          }
        },
      );
    }

    refreshStatus(query) {
      Job.get({ id: this.job.id }, (jobResponse) => {
        this.update(jobResponse);

        if (this.getStatus() === 'processing' && this.job.query_result_id && this.job.query_result_id !== 'None') {
          this.loadResult();
        } else if (this.getStatus() !== 'failed') {
          $timeout(() => {
            this.refreshStatus(query);
          }, 3000);
        }
      }, (error) => {
        logger('Connection error', error);
        // TODO: use QueryResultError, or better yet: exception/reject of promise.
        this.update({ job: { error: 'failed communicating with server. Please check your Internet connection and try again.', status: 4 } });
      });
    }

    getLink(queryId, fileType, apiKey) {
      let link = `api/queries/${queryId}/results/${this.getId()}.${fileType}`;
      if (apiKey) {
        link = `${link}?api_key=${apiKey}`;
      }
      return link;
    }

    getName(queryName, fileType) {
      return `${queryName.replace(/ /g, '_') + moment(this.getUpdatedAt()).format('_YYYY_MM_DD')}.${fileType}`;
    }

    static get(dataSourceId, query, maxAge, queryId) {
      const queryResult = new QueryResult();

      const params = { data_source_id: dataSourceId, query, max_age: maxAge };
      if (queryId !== undefined) {
        params.query_id = queryId;
      }

      QueryResultResource.post(params, (response) => {
        queryResult.update(response);

        if ('job' in response) {
          queryResult.refreshStatus(query);
        }
      }, (error) => {
        handleErrorResponse(queryResult, error);
      });

      return queryResult;
    }
  }

  return QueryResult;
}

export default function init(ngModule) {
  ngModule.factory('QueryResult', QueryResultService);
}

init.init = true;

<commit_msg>Use lower timeout for the first 5 seconds when polling for results. (#3128)<commit_after>import debug from 'debug';
import moment from 'moment';
import { sortBy, uniqBy, values, some, each, isArray, isNumber, isString, includes, forOwn } from 'lodash';

const logger = debug('redash:services:QueryResult');
const filterTypes = ['filter', 'multi-filter', 'multiFilter'];

const ALL_VALUES = '*';
const NONE_VALUES = '-';

function getColumnNameWithoutType(column) {
  let typeSplit;
  if (column.indexOf('::') !== -1) {
    typeSplit = '::';
  } else if (column.indexOf('__') !== -1) {
    typeSplit = '__';
  } else {
    return column;
  }

  const parts = column.split(typeSplit);
  if (parts[0] === '' && parts.length === 2) {
    return parts[1];
  }

  if (!includes(filterTypes, parts[1])) {
    return column;
  }

  return parts[0];
}

export function getColumnCleanName(column) {
  return getColumnNameWithoutType(column);
}

function getColumnFriendlyName(column) {
  return getColumnNameWithoutType(column).replace(/(?:^|\s)\S/g, a => a.toUpperCase());
}

function addPointToSeries(point, seriesCollection, seriesName) {
  if (seriesCollection[seriesName] === undefined) {
    seriesCollection[seriesName] = {
      name: seriesName,
      type: 'column',
      data: [],
    };
  }

  seriesCollection[seriesName].data.push(point);
}

function QueryResultService($resource, $timeout, $q, QueryResultError) {
  const QueryResultResource = $resource('api/query_results/:id', { id: '@id' }, { post: { method: 'POST' } });
  const Job = $resource('api/jobs/:id', { id: '@id' });
  const statuses = {
    1: 'waiting',
    2: 'processing',
    3: 'done',
    4: 'failed',
  };

  function handleErrorResponse(queryResult, response) {
    if (response.status === 403) {
      queryResult.update(response.data);
    } else if (response.status === 400 && 'job' in response.data) {
      queryResult.update(response.data);
    } else {
      logger('Unknown error', response);
      queryResult.update({
        job: {
          error: 'unknown error occurred. Please try again later.',
          status: 4,
        },
      });
    }
  }

  class QueryResult {
    constructor(props) {
      this.deferred = $q.defer();
      this.job = {};
      this.query_result = {};
      this.status = 'waiting';
      this.filters = undefined;
      this.filterFreeze = undefined;

      this.updatedAt = moment();

      // extended status flags
      this.isLoadingResult = false;

      if (props) {
        this.update(props);
      }
    }

    update(props) {
      Object.assign(this, props);

      if ('query_result' in props) {
        this.status = 'done';
        this.filters = undefined;
        this.filterFreeze = undefined;

        const columnTypes = {};

        // TODO: we should stop manipulating incoming data, and switch to relaying
        // on the column type set by the backend. This logic is prone to errors,
        // and better be removed. Kept for now, for backward compatability.
        each(this.query_result.data.rows, (row) => {
          forOwn(row, (v, k) => {
            let newType = null;
            if (isNumber(v)) {
              newType = 'float';
            } else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}T/)) {
              row[k] = moment.utc(v);
              newType = 'datetime';
            } else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}$/)) {
              row[k] = moment.utc(v);
              newType = 'date';
            } else if (typeof v === 'object' && v !== null) {
              row[k] = JSON.stringify(v);
            } else {
              newType = 'string';
            }

            if (newType !== null) {
              if (columnTypes[k] !== undefined && columnTypes[k] !== newType) {
                columnTypes[k] = 'string';
              } else {
                columnTypes[k] = newType;
              }
            }
          });
        });

        each(this.query_result.data.columns, (column) => {
          column.name = '' + column.name;
          if (columnTypes[column.name]) {
            if (column.type == null || column.type === 'string') {
              column.type = columnTypes[column.name];
            }
          }
        });

        this.deferred.resolve(this);
      } else if (this.job.status === 3) {
        this.status = 'processing';
      } else if (this.job.status === 4) {
        this.status = statuses[this.job.status];
        this.deferred.reject(new QueryResultError(this.job.error));
      } else {
        this.status = undefined;
      }
    }

    getId() {
      let id = null;
      if ('query_result' in this) {
        id = this.query_result.id;
      }
      return id;
    }

    cancelExecution() {
      Job.delete({ id: this.job.id });
    }

    getStatus() {
      if (this.isLoadingResult) {
        return 'loading-result';
      }
      return this.status || statuses[this.job.status];
    }

    getError() {
      // TODO: move this logic to the server...
      if (this.job.error === 'None') {
        return undefined;
      }

      return this.job.error;
    }

    getLog() {
      if (!this.query_result.data || !this.query_result.data.log || this.query_result.data.log.length === 0) {
        return null;
      }

      return this.query_result.data.log;
    }

    getUpdatedAt() {
      return this.query_result.retrieved_at || this.job.updated_at * 1000.0 || this.updatedAt;
    }

    getRuntime() {
      return this.query_result.runtime;
    }

    getRawData() {
      if (!this.query_result.data) {
        return null;
      }

      return this.query_result.data.rows;
    }

    getData() {
      if (!this.query_result.data) {
        return null;
      }

      function filterValues(filters) {
        if (!filters) {
          return null;
        }

        return filters.reduce((str, filter) => str + filter.current, '');
      }

      const filters = this.getFilters();
      const filterFreeze = filterValues(filters);

      if (this.filterFreeze !== filterFreeze) {
        this.filterFreeze = filterFreeze;

        if (filters) {
          filters.forEach((filter) => {
            if (filter.multiple && includes(filter.current, ALL_VALUES)) {
              filter.current = filter.values.slice(2);
            }

            if (filter.multiple && includes(filter.current, NONE_VALUES)) {
              filter.current = [];
            }
          });

          this.filteredData = this.query_result.data.rows.filter(row =>
            filters.reduce((memo, filter) => {
              if (!isArray(filter.current)) {
                filter.current = [filter.current];
              }

              return (
                memo &&
                some(filter.current, (v) => {
                  const value = row[filter.name];
                  if (moment.isMoment(value)) {
                    return value.isSame(v);
                  }
                  // We compare with either the value or the String representation of the value,
                  // because Select2 casts true/false to "true"/"false".
                  return v === value || String(value) === v;
                })
              );
            }, true));
        } else {
          this.filteredData = this.query_result.data.rows;
        }
      }

      return this.filteredData;
    }

    isEmpty() {
      return this.getData() === null || this.getData().length === 0;
    }

    getChartData(mapping) {
      const series = {};

      this.getData().forEach((row) => {
        let point = { $raw: row };
        let seriesName;
        let xValue = 0;
        const yValues = {};
        let eValue = null;
        let sizeValue = null;
        let zValue = null;

        forOwn(row, (v, definition) => {
          definition = '' + definition;
          const definitionParts = definition.split('::') || definition.split('__');
          const name = definitionParts[0];
          const type = mapping ? mapping[definition] : definitionParts[1];
          let value = v;

          if (type === 'unused') {
            return;
          }

          if (type === 'x') {
            xValue = value;
            point[type] = value;
          }
          if (type === 'y') {
            if (value == null) {
              value = 0;
            }
            yValues[name] = value;
            point[type] = value;
          }
          if (type === 'yError') {
            eValue = value;
            point[type] = value;
          }

          if (type === 'series') {
            seriesName = String(value);
          }

          if (type === 'size') {
            point[type] = value;
            sizeValue = value;
          }

          if (type === 'zVal') {
            point[type] = value;
            zValue = value;
          }

          if (type === 'multiFilter' || type === 'multi-filter') {
            seriesName = String(value);
          }
        });

        if (seriesName === undefined) {
          each(yValues, (yValue, ySeriesName) => {
            point = { x: xValue, y: yValue, $raw: point.$raw };
            if (eValue !== null) {
              point.yError = eValue;
            }

            if (sizeValue !== null) {
              point.size = sizeValue;
            }

            if (zValue !== null) {
              point.zVal = zValue;
            }
            addPointToSeries(point, series, ySeriesName);
          });
        } else {
          addPointToSeries(point, series, seriesName);
        }
      });
      return sortBy(values(series), 'name');
    }

    getColumns() {
      if (this.columns === undefined && this.query_result.data) {
        this.columns = this.query_result.data.columns;
      }

      return this.columns;
    }

    getColumnNames() {
      if (this.columnNames === undefined && this.query_result.data) {
        this.columnNames = this.query_result.data.columns.map(v => v.name);
      }

      return this.columnNames;
    }

    getColumnCleanNames() {
      return this.getColumnNames().map(col => getColumnCleanName(col));
    }

    getColumnFriendlyNames() {
      return this.getColumnNames().map(col => getColumnFriendlyName(col));
    }

    getFilters() {
      if (!this.filters) {
        this.prepareFilters();
      }

      return this.filters;
    }

    prepareFilters() {
      if (!this.getColumns()) {
        return;
      }

      const filters = [];

      this.getColumns().forEach((col) => {
        const name = col.name;
        const type = name.split('::')[1] || name.split('__')[1];
        if (includes(filterTypes, type)) {
          // filter found
          const filter = {
            name,
            friendlyName: getColumnFriendlyName(name),
            column: col,
            values: [],
            multiple: type === 'multiFilter' || type === 'multi-filter',
          };
          filters.push(filter);
        }
      }, this);

      this.getRawData().forEach((row) => {
        filters.forEach((filter) => {
          filter.values.push(row[filter.name]);
          if (filter.values.length === 1) {
            if (filter.multiple) {
              filter.current = [row[filter.name]];
            } else {
              filter.current = row[filter.name];
            }
          }
        });
      });

      filters.forEach((filter) => {
        if (filter.multiple) {
          filter.values.unshift(ALL_VALUES);
          filter.values.unshift(NONE_VALUES);
        }
      });

      filters.forEach((filter) => {
        filter.values = uniqBy(filter.values, (v) => {
          if (moment.isMoment(v)) {
            return v.unix();
          }
          return v;
        });
      });

      this.filters = filters;
    }

    toPromise() {
      return this.deferred.promise;
    }

    static getById(id) {
      const queryResult = new QueryResult();

      queryResult.isLoadingResult = true;
      QueryResultResource.get(
        { id },
        (response) => {
          // Success handler
          queryResult.isLoadingResult = false;
          queryResult.update(response);
        },
        (error) => {
          // Error handler
          queryResult.isLoadingResult = false;
          handleErrorResponse(queryResult, error);
        },
      );

      return queryResult;
    }

    loadResult(tryCount) {
      this.isLoadingResult = true;
      QueryResultResource.get(
        { id: this.job.query_result_id },
        (response) => {
          this.update(response);
          this.isLoadingResult = false;
        },
        (error) => {
          if (tryCount === undefined) {
            tryCount = 0;
          }

          if (tryCount > 3) {
            logger('Connection error while trying to load result', error);
            this.update({
              job: {
                error: 'failed communicating with server. Please check your Internet connection and try again.',
                status: 4,
              },
            });
            this.isLoadingResult = false;
          } else {
            $timeout(() => {
              this.loadResult(tryCount + 1);
            }, 1000 * Math.pow(2, tryCount));
          }
        },
      );
    }

    refreshStatus(query, tryNumber = 1) {
      Job.get(
        { id: this.job.id },
        (jobResponse) => {
          this.update(jobResponse);

          if (this.getStatus() === 'processing' && this.job.query_result_id && this.job.query_result_id !== 'None') {
            this.loadResult();
          } else if (this.getStatus() !== 'failed') {
            const waitTime = tryNumber > 10 ? 3000 : 500;
            $timeout(() => {
              this.refreshStatus(query, tryNumber + 1);
            }, waitTime);
          }
        },
        (error) => {
          logger('Connection error', error);
          // TODO: use QueryResultError, or better yet: exception/reject of promise.
          this.update({
            job: {
              error: 'failed communicating with server. Please check your Internet connection and try again.',
              status: 4,
            },
          });
        },
      );
    }

    getLink(queryId, fileType, apiKey) {
      let link = `api/queries/${queryId}/results/${this.getId()}.${fileType}`;
      if (apiKey) {
        link = `${link}?api_key=${apiKey}`;
      }
      return link;
    }

    getName(queryName, fileType) {
      return `${queryName.replace(/ /g, '_') + moment(this.getUpdatedAt()).format('_YYYY_MM_DD')}.${fileType}`;
    }

    static get(dataSourceId, query, maxAge, queryId) {
      const queryResult = new QueryResult();

      const params = { data_source_id: dataSourceId, query, max_age: maxAge };
      if (queryId !== undefined) {
        params.query_id = queryId;
      }

      QueryResultResource.post(
        params,
        (response) => {
          queryResult.update(response);

          if ('job' in response) {
            queryResult.refreshStatus(query);
          }
        },
        (error) => {
          handleErrorResponse(queryResult, error);
        },
      );

      return queryResult;
    }
  }

  return QueryResult;
}

export default function init(ngModule) {
  ngModule.factory('QueryResult', QueryResultService);
}

init.init = true;
<|endoftext|>


<commit_before>import React from 'react'
import {connect} from 'react-redux'
import PropTypes from 'prop-types'
import {Router, Route, Switch} from 'react-router-dom'
import PrivateRoute from './PrivateRoute'
import Hook from 'util/hook'
import config from 'gen/config-client'
import {createBrowserHistory} from 'history'

const {ADMIN_BASE_URL} = config
import LogoutContainer from 'client/containers/LogoutContainer'
import Async from 'client/components/Async'
import {
    CAPABILITY_ACCESS_ADMIN_PAGE
} from 'util/capabilities'
import {scrollByHash} from '../../../extensions/cms/util/urlUtil'

const LoginContainer = (props) => <Async {...props}
                                         load={import(/* webpackChunkName: "admin" */ '../../containers/LoginContainer')}/>
const SignUpContainer = (props) => <Async {...props}
                                          load={import(/* webpackChunkName: "admin" */ '../../containers/SignUpContainer')}/>
const UserProfileContainer = (props) => <Async {...props}
                                               load={import(/* webpackChunkName: "admin" */ '../../containers/UserProfileContainer')}/>
const SystemContainer = (props) => <Async {...props}
                                          load={import(/* webpackChunkName: "admin" */ '../../containers/SystemContainer')}/>
const BackupContainer = (props) => <Async {...props}
                                          load={import(/* webpackChunkName: "admin" */ '../../containers/BackupContainer')}/>
const FilesContainer = (props) => <Async {...props}
                                         load={import(/* webpackChunkName: "admin" */ '../../containers/FilesContainer')}/>
const TypesContainer = (props) => <Async {...props}
                                         load={import(/* webpackChunkName: "admin" */ '../../containers/TypesContainer')}/>
const HomeContainer = (props) => <Async {...props}
                                        load={import(/* webpackChunkName: "admin" */ '../../containers/HomeContainer')}/>
const ErrorPage = (props) => <Async {...props}
                                    load={import(/* webpackChunkName: "admin" */ '../../components/layout/ErrorPage')}/>

const UnauthorizedPage = (props) => (
    <ErrorPage code="401" title="Unauthorized" background="#f4a742"/>
)

class Routes extends React.Component {

    adminBaseUrlPlain = ADMIN_BASE_URL.slice(1)
    history = createBrowserHistory()

    routes = [
        {exact: true, private: true, path: ADMIN_BASE_URL + '/', component: HomeContainer},
        {exact: true, private: true, path: ADMIN_BASE_URL + '/home/:name*', component: HomeContainer},
        {path: ADMIN_BASE_URL + '/login', component: LoginContainer},
        {path: ADMIN_BASE_URL + '/logout', component: LogoutContainer},
        {path: ADMIN_BASE_URL + '/signup', component: SignUpContainer},
        {exact: true, private: true, path: ADMIN_BASE_URL + '/types/:type*', component: TypesContainer},
        {private: true, path: ADMIN_BASE_URL + '/profile', component: UserProfileContainer},
        {private: true, path: ADMIN_BASE_URL + '/system', component: SystemContainer},
        {private: true, path: ADMIN_BASE_URL + '/backup', component: BackupContainer},
        {private: true, path: ADMIN_BASE_URL + '/files', component: FilesContainer}
    ]

    constructor(props) {
        super(props)

        Hook.call('Routes', {routes: this.routes, container: this})
        this.history._urlStack = [location.href.substring(location.origin.length)]
        // override push and replace methode to prepend language code if needed
        this.history._replace = this.history.replace
        this.history._push = this.history.push
        this.history.push = (path, state) => {

            let newPath
            if (path.constructor === Object)
                path = path.pathname
            if (path.indexOf('#') !== 0 && path.split('?')[0].split('#')[0] !== _app_.contextPath && path.indexOf(_app_.contextPath + '/') !== 0) {
                newPath = _app_.contextPath + path
            } else {
                newPath = path
            }

            if (!this.history._urlStack) {
                this.history._urlStack = []
            }
            const index = this.history._urlStack.indexOf(path)
            if (index >= 0) {
                this.history._urlStack.splice(index, 1);
            }

            this.history._urlStack.unshift(path)
            if (this.history._urlStack.length > 10) {
                this.history._urlStack = this.history._urlStack.slice(0, 9)
            }
            this.history._last = this.history.location.pathname
            // encodeURI again as it gets decoded in react routing
            this.history._push(encodeURI(newPath), state)
        }
        this.history.replace = (o, state) => {

            if (o.constructor !== Object) {
                o = {pathname: o}
            }

            if (o.pathname !== _app_.contextPath && o.pathname.indexOf(_app_.contextPath + '/') < 0) {
                o.pathname = _app_.contextPath + o.pathname
            }
            this.history._replace(o, state)
        }

        scrollByHash(location.href, {})

    }

    render() {
        const {user: {isAuthenticated, userData}} = this.props
        const capabilities = (userData && userData.role && userData.role.capabilities) || []
        return <Router history={this.history}>
            <Switch>
                {_app_.redirect404 !== location.pathname && this.routes.map((o, i) => {
                    if (!isAuthenticated || !o.path.startsWith(ADMIN_BASE_URL) ||
                        o.path.startsWith(ADMIN_BASE_URL + '/login') ||
                        o.path.startsWith(ADMIN_BASE_URL + '/profile') ||
                        o.path.startsWith(ADMIN_BASE_URL + '/types') ||
                        o.path.startsWith(ADMIN_BASE_URL + '/logout') ||
                        capabilities.indexOf(CAPABILITY_ACCESS_ADMIN_PAGE) >= 0) {
                        if (o.private) {
                            return <PrivateRoute key={i} path={_app_.contextPath + o.path}
                                                 isAuthenticated={isAuthenticated}
                                                 exact={o.exact}
                                                 component={o.component}/>
                        } else {
                            return <Route key={i} path={_app_.contextPath + o.path} exact={o.exact}
                                          component={o.component}
                                          render={o.render}/>
                        }
                    } else {
                        return <Route key={i} path={_app_.contextPath + o.path} exact={o.exact}
                                      component={UnauthorizedPage}/>
                    }
                })}
                <Route component={ErrorPage}/>
            </Switch>
        </Router>
    }
}

Routes.propTypes = {
    user: PropTypes.object.isRequired
}

/**
 * Map the state to props.
 */
const mapStateToProps = (store) => {
    return {
        user: store.user
    }
}

/**
 * Connect the component to
 * the Redux store.
 */
export default connect(
    mapStateToProps
)(Routes)
<commit_msg>Fix Routes with search issue<commit_after>import React from 'react'
import {connect} from 'react-redux'
import PropTypes from 'prop-types'
import {Router, Route, Switch} from 'react-router-dom'
import PrivateRoute from './PrivateRoute'
import Hook from 'util/hook'
import config from 'gen/config-client'
import {createBrowserHistory} from 'history'

const {ADMIN_BASE_URL} = config
import LogoutContainer from 'client/containers/LogoutContainer'
import Async from 'client/components/Async'
import {
    CAPABILITY_ACCESS_ADMIN_PAGE
} from 'util/capabilities'
import {scrollByHash} from '../../../extensions/cms/util/urlUtil'

const LoginContainer = (props) => <Async {...props}
                                         load={import(/* webpackChunkName: "admin" */ '../../containers/LoginContainer')}/>
const SignUpContainer = (props) => <Async {...props}
                                          load={import(/* webpackChunkName: "admin" */ '../../containers/SignUpContainer')}/>
const UserProfileContainer = (props) => <Async {...props}
                                               load={import(/* webpackChunkName: "admin" */ '../../containers/UserProfileContainer')}/>
const SystemContainer = (props) => <Async {...props}
                                          load={import(/* webpackChunkName: "admin" */ '../../containers/SystemContainer')}/>
const BackupContainer = (props) => <Async {...props}
                                          load={import(/* webpackChunkName: "admin" */ '../../containers/BackupContainer')}/>
const FilesContainer = (props) => <Async {...props}
                                         load={import(/* webpackChunkName: "admin" */ '../../containers/FilesContainer')}/>
const TypesContainer = (props) => <Async {...props}
                                         load={import(/* webpackChunkName: "admin" */ '../../containers/TypesContainer')}/>
const HomeContainer = (props) => <Async {...props}
                                        load={import(/* webpackChunkName: "admin" */ '../../containers/HomeContainer')}/>
const ErrorPage = (props) => <Async {...props}
                                    load={import(/* webpackChunkName: "admin" */ '../../components/layout/ErrorPage')}/>

const UnauthorizedPage = (props) => (
    <ErrorPage code="401" title="Unauthorized" background="#f4a742"/>
)

class Routes extends React.Component {

    adminBaseUrlPlain = ADMIN_BASE_URL.slice(1)
    history = createBrowserHistory()

    routes = [
        {exact: true, private: true, path: ADMIN_BASE_URL + '/', component: HomeContainer},
        {exact: true, private: true, path: ADMIN_BASE_URL + '/home/:name*', component: HomeContainer},
        {path: ADMIN_BASE_URL + '/login', component: LoginContainer},
        {path: ADMIN_BASE_URL + '/logout', component: LogoutContainer},
        {path: ADMIN_BASE_URL + '/signup', component: SignUpContainer},
        {exact: true, private: true, path: ADMIN_BASE_URL + '/types/:type*', component: TypesContainer},
        {private: true, path: ADMIN_BASE_URL + '/profile', component: UserProfileContainer},
        {private: true, path: ADMIN_BASE_URL + '/system', component: SystemContainer},
        {private: true, path: ADMIN_BASE_URL + '/backup', component: BackupContainer},
        {private: true, path: ADMIN_BASE_URL + '/files', component: FilesContainer}
    ]

    constructor(props) {
        super(props)

        Hook.call('Routes', {routes: this.routes, container: this})
        this.history._urlStack = [location.href.substring(location.origin.length)]
        // override push and replace methode to prepend language code if needed
        this.history._replace = this.history.replace
        this.history._push = this.history.push
        this.history.push = (path, state) => {

            let newPath
            if (path.constructor === Object)
                path = path.pathname
            if (path.indexOf('#') !== 0 && path.split('?')[0].split('#')[0] !== _app_.contextPath && path.indexOf(_app_.contextPath + '/') !== 0) {
                newPath = _app_.contextPath + path
            } else {
                newPath = path
            }

            if (!this.history._urlStack) {
                this.history._urlStack = []
            }
            const index = this.history._urlStack.indexOf(path)
            if (index >= 0) {
                this.history._urlStack.splice(index, 1);
            }

            this.history._urlStack.unshift(path)
            if (this.history._urlStack.length > 10) {
                this.history._urlStack = this.history._urlStack.slice(0, 9)
            }
            this.history._last = this.history.location.pathname
            // encodeURI again as it gets decoded in react routing
            this.history._push(encodeURI(newPath), state)
        }
        this.history.replace = (o, state) => {

            if (o.constructor !== Object) {
                o = {pathname: o}
            }

            if (o.pathname !== _app_.contextPath && o.pathname.indexOf(_app_.contextPath + '/') < 0) {
                o.pathname = _app_.contextPath + o.pathname
            }

            if(o.pathname.indexOf('?')>=0 && !o.search){
                const spl = o.pathname.split('?')
                o.pathname = spl[0]
                o.search = '?'+spl[1]
            }

            this.history._replace(o, state)
        }

        scrollByHash(location.href, {})

    }

    render() {
        const {user: {isAuthenticated, userData}} = this.props
        const capabilities = (userData && userData.role && userData.role.capabilities) || []
        return <Router history={this.history}>
            <Switch>
                {_app_.redirect404 !== location.pathname && this.routes.map((o, i) => {
                    if (!isAuthenticated || !o.path.startsWith(ADMIN_BASE_URL) ||
                        o.path.startsWith(ADMIN_BASE_URL + '/login') ||
                        o.path.startsWith(ADMIN_BASE_URL + '/profile') ||
                        o.path.startsWith(ADMIN_BASE_URL + '/types') ||
                        o.path.startsWith(ADMIN_BASE_URL + '/logout') ||
                        capabilities.indexOf(CAPABILITY_ACCESS_ADMIN_PAGE) >= 0) {
                        if (o.private) {
                            return <PrivateRoute key={i} path={_app_.contextPath + o.path}
                                                 isAuthenticated={isAuthenticated}
                                                 exact={o.exact}
                                                 component={o.component}/>
                        } else {
                            return <Route key={i} path={_app_.contextPath + o.path} exact={o.exact}
                                          component={o.component}
                                          render={o.render}/>
                        }
                    } else {
                        return <Route key={i} path={_app_.contextPath + o.path} exact={o.exact}
                                      component={UnauthorizedPage}/>
                    }
                })}
                <Route component={ErrorPage}/>
            </Switch>
        </Router>
    }
}

Routes.propTypes = {
    user: PropTypes.object.isRequired
}

/**
 * Map the state to props.
 */
const mapStateToProps = (store) => {
    return {
        user: store.user
    }
}

/**
 * Connect the component to
 * the Redux store.
 */
export default connect(
    mapStateToProps
)(Routes)
<|endoftext|>


<commit_before>var template;
var user;
var grantid;

var loadDepartmentNavigation = function(overview) {
  if(overview.user.permissions.level == 1) {
    var stages = ['Research', 'Internal', 'ASU', 'Complete'];

    overview.departmentNames = stages;
    overview.currentDepartment = overview.user.permissions.stage;
  }
};

var loadDepartment = function(grantID, stageIndex) {
  var query = '/setAdminStage?stage=' + stageIndex;
  $.ajax({
    url: query,
    type: 'POST',
    contentType: 'application/json',
    success: function() {
      window.location = '/detail/' + grantID;
    }
  });
};

var editGrant = function (grantid) {
  jsonObj = {
    title: $('#grantNameEdit').val(),
    description: $('#grantDescriptionEdit').summernote('code'),
    url: $('#grantUrlEdit').val()
  };

  $.ajax({
    url: '/grants/' + grantid,
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({
      $set: jsonObj
    }),
  }).done(function() {
    window.location.reload(true);
  });
};

var insertGrant = function (myGrant) {
  $.ajax({
    url: '/grants',
    type: 'PUT',
    data: JSON.stringify(myGrant),
    contentType: 'application/json',
    success: function(result) {
      var updateObj = {$addToSet: {grantIds: result.insertedIds[0]}};

      $.ajax({
        url: '/users/' + user._id,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify(updateObj),
        success: function() {
          // window.location.reload(true);
          window.location = '/detail/' + result.insertedIds[0];
        }
      });
    }
  });
};

var createGrant = function (myGrant, cardTemplates) {
  if (cardTemplates)
    $.ajax({
      url: '/cards',
      type: 'PUT',
      data: JSON.stringify(cardTemplates),
      contentType: 'application/json',
    }).done(function(result) {
      myGrant.cardCount = result.insertedCount;
      myGrant.stages[0].toDo = result.insertedIds;
    }).then(function () {
      insertGrant(myGrant);
    });
  else insertGrant(myGrant);
};

// var insertTemplateCards = function (templateObj, callback) {
//   $.ajax({
//     url: '/cards',
//     type: 'PUT',
//     data: JSON.stringify(templateObj),
//     contentType: 'application/json',
//   }).done(function(card) {
//     callback(card._id);
//   });
// };

$(function() {
  $.ajax({
    url: 'getOverview',
    type: 'GET',
    dataType: 'json',
    success: function(overview) {
      user = overview.user;
      loadNavbar(user);

      // Remove delete privileges for non admins
      if (user.permissions.level !== 1) {
        $('#grantEditDelete').remove();
      }

      loadDepartmentNavigation(overview);

      $.ajax({
        url : 'templates/overviewPanel.html',
        dataType: 'html',
        method: 'GET',
        success: function(data) {
          template = Handlebars.compile(data);
          $("#overviewContent").append(template(overview));
          $('[data-toggle="tooltip"]').tooltip({container:'body'});
        }
      });
    }
  });

  $('#grantGen * [data-toggle="popover"]').popover({
    container:'body',
    html : true
  });

  $('#grantEdit').on('show.bs.modal', function (event) {
    var button = $(event.relatedTarget); // Button that triggered the modal
    grantid = button.data('grantid'); // Extract info from data-* attributes
    var modal = $(this);

    $.ajax({
      url: '/grants/' + grantid,
      type: 'GET',
      dataType: 'json'
    }).done(function(data) {
      $('#grantNameEdit').val(data.title);
      $('#grantUrlEdit').val(data.url);
      $('#grantDescriptionEdit').summernote('code', data.description);
      $('#grantEditSubmit').click(function () {
        editGrant(grantid);
      });
      // $('#grantEditDismiss').click(function () {
      //   $('#grantEditSubmit').unbind();
      // })
    });
  });

  $('#cardGenCreate').click(function () {
    var grantDescription = $('#grantDescription').summernote('code');
    var grantName = $('#grantName').val();
    var grantUrl = $('#grantUrl').val();
    var grantTemplate = parseInt($('#grantTemplate').val(), 10);
    var templates = [
      [{"title":"Cover Sheet","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Project Description","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"References Cited","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Biosketch","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Departmental Letter","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Letters of Collaboration (optional)","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Budget","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Budget Justification","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]}]
      //add more temaplate here is needed
    ];

    var myGrant = {
      title: grantName,
      description: grantDescription,
      url: grantUrl,
      users: [],
      cardCount: 0,
      stages: [{
        toDo: [],
        inProgress: [],
        complete: []
      }, {
        toDo: [],
        inProgress: [],
        complete: []
      }, {
        toDo: [],
        inProgress: [],
        complete: []
      }, {
        cards: []
      }]
    };

    var cardTemplate = {
      cardCount: 0,
      cardIds: []
    };

    switch (grantTemplate) {
      case 0:
        createGrant(myGrant);
        break;
      case 1:
        // for (card in templates[0])
        //   insertTemplateCards(templates[0][card], function(cardId) {
        //     cardTemplate.cardCount += 1;
        //     cardTemplate.cardIds.push(cardId);
        //   });
        createGrant(myGrant, templates[0]);
        break;
      // add more cases here if needed
      default:
        break;
    }

    // console.log(myGrant);
    // createGrant(myGrant, cardTemplate);
  });

  $('#modalDelete').on('hidden.bs.modal', function() {
    $('#grantEdit').modal('hide');
  });

  $('#confirmDeleteButton').click(function() {
    $.ajax({
      url: '/grants/' + grantid,
      type: 'DELETE',
      contentType: 'application/json',
      success: function() {
        window.location.reload(true);
      }
    });
  });
});
<commit_msg>just removing commented/unused code.<commit_after>var template;
var user;
var grantid;

var loadDepartmentNavigation = function(overview) {
  if(overview.user.permissions.level == 1) {
    var stages = ['Research', 'Internal', 'ASU', 'Complete'];

    overview.departmentNames = stages;
    overview.currentDepartment = overview.user.permissions.stage;
  }
};

var loadDepartment = function(grantID, stageIndex) {
  var query = '/setAdminStage?stage=' + stageIndex;
  $.ajax({
    url: query,
    type: 'POST',
    contentType: 'application/json',
    success: function() {
      window.location = '/detail/' + grantID;
    }
  });
};

var editGrant = function (grantid) {
  jsonObj = {
    title: $('#grantNameEdit').val(),
    description: $('#grantDescriptionEdit').summernote('code'),
    url: $('#grantUrlEdit').val()
  };

  $.ajax({
    url: '/grants/' + grantid,
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({
      $set: jsonObj
    }),
  }).done(function() {
    window.location.reload(true);
  });
};

var insertGrant = function (myGrant) {
  $.ajax({
    url: '/grants',
    type: 'PUT',
    data: JSON.stringify(myGrant),
    contentType: 'application/json',
    success: function(result) {
      var updateObj = {$addToSet: {grantIds: result.insertedIds[0]}};

      $.ajax({
        url: '/users/' + user._id,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify(updateObj),
        success: function() {
          // window.location.reload(true);
          window.location = '/detail/' + result.insertedIds[0];
        }
      });
    }
  });
};

var createGrant = function (myGrant, cardTemplates) {
  if (cardTemplates)
    $.ajax({
      url: '/cards',
      type: 'PUT',
      data: JSON.stringify(cardTemplates),
      contentType: 'application/json',
    }).done(function(result) {
      myGrant.cardCount = result.insertedCount;
      myGrant.stages[0].toDo = result.insertedIds;
    }).then(function () {
      insertGrant(myGrant);
    });
  else insertGrant(myGrant);
};

$(function() {
  $.ajax({
    url: 'getOverview',
    type: 'GET',
    dataType: 'json',
    success: function(overview) {
      user = overview.user;
      loadNavbar(user);

      // Remove delete privileges for non admins
      if (user.permissions.level !== 1) {
        $('#grantEditDelete').remove();
      }

      loadDepartmentNavigation(overview);

      $.ajax({
        url : 'templates/overviewPanel.html',
        dataType: 'html',
        method: 'GET',
        success: function(data) {
          template = Handlebars.compile(data);
          $("#overviewContent").append(template(overview));
          $('[data-toggle="tooltip"]').tooltip({container:'body'});
        }
      });
    }
  });

  $('#grantGen * [data-toggle="popover"]').popover({
    container:'body',
    html : true
  });

  $('#grantEdit').on('show.bs.modal', function (event) {
    var button = $(event.relatedTarget); // Button that triggered the modal
    grantid = button.data('grantid'); // Extract info from data-* attributes
    var modal = $(this);

    $.ajax({
      url: '/grants/' + grantid,
      type: 'GET',
      dataType: 'json'
    }).done(function(data) {
      $('#grantNameEdit').val(data.title);
      $('#grantUrlEdit').val(data.url);
      $('#grantDescriptionEdit').summernote('code', data.description);
      $('#grantEditSubmit').click(function () {
        editGrant(grantid);
      });
      // $('#grantEditDismiss').click(function () {
      //   $('#grantEditSubmit').unbind();
      // })
    });
  });

  $('#cardGenCreate').click(function () {
    var grantDescription = $('#grantDescription').summernote('code');
    var grantName = $('#grantName').val();
    var grantUrl = $('#grantUrl').val();
    var grantTemplate = parseInt($('#grantTemplate').val(), 10);
    var templates = [
      [{"title":"Cover Sheet","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Project Description","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"References Cited","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Biosketch","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Departmental Letter","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Letters of Collaboration (optional)","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Budget","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]},{"title":"Budget Justification","documentUrl":"","tags":[],"userIds":[],"lock":[false,false,false,false]}]
      //add more temaplate here is needed
    ];

    var myGrant = {
      title: grantName,
      description: grantDescription,
      url: grantUrl,
      users: [],
      cardCount: 0,
      stages: [{
        toDo: [],
        inProgress: [],
        complete: []
      }, {
        toDo: [],
        inProgress: [],
        complete: []
      }, {
        toDo: [],
        inProgress: [],
        complete: []
      }, {
        cards: []
      }]
    };

    var cardTemplate = {
      cardCount: 0,
      cardIds: []
    };

    switch (grantTemplate) {
      case 0:
        createGrant(myGrant);
        break;
      case 1:
        // for (card in templates[0])
        //   insertTemplateCards(templates[0][card], function(cardId) {
        //     cardTemplate.cardCount += 1;
        //     cardTemplate.cardIds.push(cardId);
        //   });
        createGrant(myGrant, templates[0]);
        break;
      // add more cases here if needed
      default:
        break;
    }

    // console.log(myGrant);
    // createGrant(myGrant, cardTemplate);
  });

  $('#modalDelete').on('hidden.bs.modal', function() {
    $('#grantEdit').modal('hide');
  });

  $('#confirmDeleteButton').click(function() {
    $.ajax({
      url: '/grants/' + grantid,
      type: 'DELETE',
      contentType: 'application/json',
      success: function() {
        window.location.reload(true);
      }
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Controller for handling the dialog for editing profiles
 */
module.exports = function ($mdDialog) {

  this.friendCodes = this.data.friendCodes ? this.data.friendCodes.slice(0) : [''];
  this.inGameNames = this.data.inGameNames ? this.data.inGameNames.slice(0) : [''];
  this.tsvs = this.data.trainerShinyValues ? this.data.trainerShinyValues.slice(0) : [''];

  if (this.friendCodes.length === 0) {
    this.friendCodes = [''];
  }
  if (this.inGameNames.length === 0) {
    this.inGameNames = [''];
  }
  if (this.tsvs.length === 0) {
    this.tsvs = [''];
  }

  this.addFC = () => this.friendCodes.push('');
  this.addIGN = () => this.inGameNames.push('');
  this.addTSV = () => this.tsvs.push('');

  this.cancel = () => {
    $mdDialog.cancel();
  };

  this.answer = () => {
    $mdDialog.hide({
      inGameNames: this.inGameNames.filter(removeEmpty),
      friendCodes: this.friendCodes.filter(removeEmpty),
      trainerShinyValues: this.tsvs.filter(removeEmpty)
    });
  };

  const removeEmpty = (el) => el !== '';
};
<commit_msg>Use prototype functions in profile-edit.ctrl.js<commit_after>/**
 * Controller for handling the dialog for editing profiles
 */
module.exports = class ProfileEdit {
  constructor ($mdDialog) {
    this.$mdDialog = $mdDialog;

    this.friendCodes = this.data.friendCodes ? this.data.friendCodes.slice(0) : [''];
    this.inGameNames = this.data.inGameNames ? this.data.inGameNames.slice(0) : [''];
    this.tsvs = this.data.trainerShinyValues ? this.data.trainerShinyValues.slice(0) : [''];

    if (this.friendCodes.length === 0) {
      this.friendCodes = [''];
    }
    if (this.inGameNames.length === 0) {
      this.inGameNames = [''];
    }
    if (this.tsvs.length === 0) {
      this.tsvs = [''];
    }
  }
  addFC () {
    this.friendCodes.push('');
  }
  addIGN () {
    this.inGameNames.push('');
  }
  addTSV () {
    this.tsvs.push('');
  }
  cancel () {
    this.$mdDialog.cancel();
  }
  answer () {
    this.$mdDialog.hide({
      inGameNames: this.inGameNames.filter(ign => ign),
      friendCodes: this.friendCodes.filter(fc => fc),
      trainerShinyValues: this.tsvs.filter(tsv => tsv)
    });
  }
};
<|endoftext|>


<commit_before>import { createSelector, createStructuredSelector } from 'reselect';
import qs from 'query-string';
import { uniqBy, has } from 'lodash';
import {
  getYColumnValue,
  getThemeConfig,
  getTooltipConfig,
  setChartColors,
  getColumnValue,
  getPieChartThemeConfig
} from 'utils/graphs';
import {
  CHART_COLORS,
  CHART_COLORS_EXTENDED,
  GREY_CHART_COLORS,
  DEFAULT_AXES_CONFIG
} from 'data/constants';
import { format } from 'd3-format';

const getSourceSelection = state =>
  (state.location && state.location.search) || null;
const getCountries = state => state.countries || null;
const getRegions = state => state.regions || null;
const getAgricultureEmissionsData = state =>
  (state.agricultureEmissions && state.agricultureEmissions.data) || null;
const getAgricultureEmissionsLoading = state =>
  (state.agricultureEmissions && state.agricultureEmissions.loading) || false;
const getGhgEmissionsData = state =>
  (state.ghgEmissions && state.ghgEmissions.data) || null;
const getGhgEmissionsLoading = state =>
  (state.ghgEmissions && state.ghgEmissions.loading) || false;
const getGhgSources = state =>
  (has(state, 'ghgEmissionsMeta.meta.data_source') &&
    state.ghgEmissionsMeta.meta.data_source) ||
  null;
const getGhgGas = state =>
  (has(state, 'ghgEmissionsMeta.meta.gas') &&
    state.ghgEmissionsMeta.meta.gas) ||
  null;

const API_SCALE = 0.001; // converting from Gigagrams to Megatonnes ( 1 Gg = 0.001 Mt)
const AGRICULTURE_COLOR = '#0677B3';

/**  LOCATIONS SELECTORS */
const getCountriesOptions = createSelector([getCountries], countries => {
  if (!countries) return null;
  return countries.map(d => ({
    label: d.wri_standard_name,
    value: d.iso_code3
  }));
});

const getRegionsOptions = createSelector([getRegions], regions => {
  if (!regions) return null;
  return regions.map(d => ({
    label: d.wri_standard_name,
    value: d.iso_code3
  }));
});

const getLocationsOptions = createSelector(
  [getCountriesOptions, getRegionsOptions],
  (countries, regions) => {
    if (!countries || !countries.length || !regions || !regions.length) {
      return [];
    }
    return [...countries, ...regions];
  }
);

const getGhgEmissionGas = createSelector([getGhgGas], gases => {
  if (!gases) return null;
  const defaultGas = gases.find(s => s.label === 'All GHG');
  return defaultGas || gases[0];
});

const getGhgEmissionSource = createSelector([getGhgSources], sources => {
  if (!sources) return null;
  const defaultSource = sources.find(s => s.label === 'CAIT');
  return defaultSource || sources[0];
});

const getEmissionCountrySelected = createSelector(
  [getSourceSelection, getLocationsOptions],
  (selectedEmissionOption, countriesOptions) => {
    if (!countriesOptions) return null;
    const defaultCountry = countriesOptions.find(
      ({ value }) => value === 'WORLD'
    );
    if (!selectedEmissionOption) {
      return defaultCountry || countriesOptions[0];
    }
    const { emissionsCountry } = qs.parse(selectedEmissionOption);
    const selectedCountry = countriesOptions.find(
      ({ value }) => value === emissionsCountry
    );
    return selectedCountry || defaultCountry;
  }
);

/** LINE CHART SELECTORS */
const getAgricultureSubsectorsData = createSelector(
  [getAgricultureEmissionsData],
  data => {
    if (!data) return null;
    return data.filter(
      ({ emission_subcategory: { category_id } }) => category_id === 1
    );
  }
);

const getChartData = createSelector([getAgricultureSubsectorsData], data => {
  if (!data || !data.length) return null;
  const xValues = Object.keys(data[0].values).map(key => parseInt(key, 10));
  const dataParsed = xValues.map(x => {
    const yItems = {};
    data.forEach(d => {
      const yKey = getYColumnValue(d.emission_subcategory.name);
      const yData = d.values[x];
      yItems[yKey] = yData ? parseFloat(yData) * API_SCALE : undefined;
    });
    return { x, ...yItems };
  });
  return dataParsed;
});

const getChartDomain = createSelector([getChartData], data => {
  if (!data) return null;
  return { x: ['auto', 'auto'], y: [0, 'auto'] };
});

const getYColumns = createSelector([getAgricultureSubsectorsData], data => {
  if (!data || !data.length) return null;
  const yColumns = data
    .map(({ emission_subcategory: { name } }) => ({
      label: name,
      value: getYColumnValue(name)
    }))
    .filter(y => y !== 'x');
  const yUniqColumns = uniqBy(yColumns, 'value');
  return yUniqColumns;
});

let colorThemeCache = {};

const getChartConfig = createSelector(
  [getChartData, getYColumns],
  (data, yColumns) => {
    if (!data || !yColumns) return null;

    const yColumnsChecked = yColumns;
    const chartColors = setChartColors(
      yColumnsChecked.length,
      CHART_COLORS,
      CHART_COLORS_EXTENDED
    );
    const theme = getThemeConfig(yColumnsChecked, chartColors);
    colorThemeCache = { ...theme, ...colorThemeCache };
    const tooltip = getTooltipConfig(yColumnsChecked);
    return {
      axes: {
        ...DEFAULT_AXES_CONFIG,
        yLeft: { ...DEFAULT_AXES_CONFIG.yLeft, unit: 'MtCO2e' }
      },
      theme: colorThemeCache,
      tooltip,
      columns: {
        x: [{ label: 'emission', value: 'x' }],
        y: yColumns
      }
    };
  }
);

const getFilterOptions = createSelector([getYColumns], yColumns => {
  if (!yColumns) return null;
  return yColumns.map(column => ({ ...column, groupId: 'subSectors' }));
});

/** PIE-CHART SELECTORS */
const getGhgEmissionsFilter = createSelector(
  [getEmissionCountrySelected, getGhgEmissionSource, getGhgEmissionGas],
  (selectedCountry, source, gas) => {
    if (!selectedCountry || !source) return null;
    return {
      location: selectedCountry.value,
      source: source.value,
      gas: gas.value
    };
  }
);

export const getPieChartData = createSelector([getGhgEmissionsData], data => {
  if (!data || !data.length) return null;

  const sectorsLastYearEmission = data
    .map(({ emissions, sector, location }) => {
      const lastYearEmission = emissions[emissions.length - 1];
      return { sector, location, ...lastYearEmission };
    })
    .filter(({ value }) => value > 0); // filter for negative emission for Forestry sector

  if (!sectorsLastYearEmission) return null;
  const totalEmission = sectorsLastYearEmission.reduce(
    (acc, emission) => acc + emission.value,
    0
  );

  const sectorEmissions = sectorsLastYearEmission.map(({ sector, value }) => ({
    name: getColumnValue(sector).toLowerCase(),
    value,
    sector,
    formattedValue: `${format('.2s')(value)}`,
    formattedPercentage: `${format('.2f')(value * 100 / totalEmission)}%`,
    percentageValue: value * 100 / totalEmission
  }));

  const agricultureRow = sectorEmissions.find(
    ({ name }) => name === 'agriculture'
  );
  const sectorsEmissionsData = agricultureRow
    ? [
      agricultureRow,
      ...sectorEmissions.filter(({ name }) => name !== 'agriculture')
    ]
    : sectorEmissions;
  const { location, year } = sectorsLastYearEmission[0];

  return {
    year,
    location,
    emissionValue: agricultureRow && agricultureRow.formattedValue,
    emissionPercentage: agricultureRow && agricultureRow.formattedPercentage,
    data: sectorsEmissionsData
  };
});

const getPieChartConfig = createSelector([getPieChartData], pieChartData => {
  if (!pieChartData || !pieChartData.data || !pieChartData.data.length) {
    return null;
  }
  const { data } = pieChartData;
  const columns = data.map(({ sector }) => ({
    label: sector,
    value: getColumnValue(sector).toLowerCase()
  }));

  const tooltip = getTooltipConfig(columns);
  let theme = getPieChartThemeConfig(columns, GREY_CHART_COLORS);
  const agricultureTheme = {
    ...theme.agriculture,
    stroke: AGRICULTURE_COLOR
  };
  theme = { ...theme, agriculture: agricultureTheme };

  const config = {
    tooltip,
    animation: true,
    theme,
    innerRadius: 50,
    outerRadius: 90,
    hideLabel: true,
    hideLegend: true
  };

  return config;
});

const getPieChartPayload = createSelector(
  [getPieChartData, getPieChartConfig, getGhgEmissionsLoading],
  (pieChartData, config, loading) => {
    if (!pieChartData || !config) return null;

    const {
      location,
      year,
      emissionValue,
      emissionPercentage,
      data
    } = pieChartData;
    const color = AGRICULTURE_COLOR;

    return {
      location,
      year: `${year}`,
      emissionValue,
      emissionPercentage,
      data,
      config,
      color,
      loading
    };
  }
);

// TO DO
// const getButtonGroupConfig = createSelector();

export const getAllData = createStructuredSelector({
  data: getChartData,
  loading: getAgricultureEmissionsLoading,
  config: getChartConfig,
  domain: getChartDomain,
  filters: getFilterOptions,
  locations: getLocationsOptions,
  emissionsCountry: getEmissionCountrySelected,
  ghgEmissionsFilters: getGhgEmissionsFilter,
  pieChartData: getPieChartPayload
});
<commit_msg>Add 'gas' guard to ghg emissions filter selector<commit_after>import { createSelector, createStructuredSelector } from 'reselect';
import qs from 'query-string';
import { uniqBy, has } from 'lodash';
import {
  getYColumnValue,
  getThemeConfig,
  getTooltipConfig,
  setChartColors,
  getColumnValue,
  getPieChartThemeConfig
} from 'utils/graphs';
import {
  CHART_COLORS,
  CHART_COLORS_EXTENDED,
  GREY_CHART_COLORS,
  DEFAULT_AXES_CONFIG
} from 'data/constants';
import { format } from 'd3-format';

const getSourceSelection = state =>
  (state.location && state.location.search) || null;
const getCountries = state => state.countries || null;
const getRegions = state => state.regions || null;
const getAgricultureEmissionsData = state =>
  (state.agricultureEmissions && state.agricultureEmissions.data) || null;
const getAgricultureEmissionsLoading = state =>
  (state.agricultureEmissions && state.agricultureEmissions.loading) || false;
const getGhgEmissionsData = state =>
  (state.ghgEmissions && state.ghgEmissions.data) || null;
const getGhgEmissionsLoading = state =>
  (state.ghgEmissions && state.ghgEmissions.loading) || false;
const getGhgSources = state =>
  (has(state, 'ghgEmissionsMeta.meta.data_source') &&
    state.ghgEmissionsMeta.meta.data_source) ||
  null;
const getGhgGas = state =>
  (has(state, 'ghgEmissionsMeta.meta.gas') &&
    state.ghgEmissionsMeta.meta.gas) ||
  null;

const API_SCALE = 0.001; // converting from Gigagrams to Megatonnes ( 1 Gg = 0.001 Mt)
const AGRICULTURE_COLOR = '#0677B3';

/**  LOCATIONS SELECTORS */
const getCountriesOptions = createSelector([getCountries], countries => {
  if (!countries) return null;
  return countries.map(d => ({
    label: d.wri_standard_name,
    value: d.iso_code3
  }));
});

const getRegionsOptions = createSelector([getRegions], regions => {
  if (!regions) return null;
  return regions.map(d => ({
    label: d.wri_standard_name,
    value: d.iso_code3
  }));
});

const getLocationsOptions = createSelector(
  [getCountriesOptions, getRegionsOptions],
  (countries, regions) => {
    if (!countries || !countries.length || !regions || !regions.length) {
      return [];
    }
    return [...countries, ...regions];
  }
);

const getGhgEmissionGas = createSelector([getGhgGas], gases => {
  if (!gases) return null;
  const defaultGas = gases.find(s => s.label === 'All GHG');
  return defaultGas || gases[0];
});

const getGhgEmissionSource = createSelector([getGhgSources], sources => {
  if (!sources) return null;
  const defaultSource = sources.find(s => s.label === 'CAIT');
  return defaultSource || sources[0];
});

const getEmissionCountrySelected = createSelector(
  [getSourceSelection, getLocationsOptions],
  (selectedEmissionOption, countriesOptions) => {
    if (!countriesOptions) return null;
    const defaultCountry = countriesOptions.find(
      ({ value }) => value === 'WORLD'
    );
    if (!selectedEmissionOption) {
      return defaultCountry || countriesOptions[0];
    }
    const { emissionsCountry } = qs.parse(selectedEmissionOption);
    const selectedCountry = countriesOptions.find(
      ({ value }) => value === emissionsCountry
    );
    return selectedCountry || defaultCountry;
  }
);

/** LINE CHART SELECTORS */
const getAgricultureSubsectorsData = createSelector(
  [getAgricultureEmissionsData],
  data => {
    if (!data) return null;
    return data.filter(
      ({ emission_subcategory: { category_id } }) => category_id === 1
    );
  }
);

const getChartData = createSelector([getAgricultureSubsectorsData], data => {
  if (!data || !data.length) return null;
  const xValues = Object.keys(data[0].values).map(key => parseInt(key, 10));
  const dataParsed = xValues.map(x => {
    const yItems = {};
    data.forEach(d => {
      const yKey = getYColumnValue(d.emission_subcategory.name);
      const yData = d.values[x];
      yItems[yKey] = yData ? parseFloat(yData) * API_SCALE : undefined;
    });
    return { x, ...yItems };
  });
  return dataParsed;
});

const getChartDomain = createSelector([getChartData], data => {
  if (!data) return null;
  return { x: ['auto', 'auto'], y: [0, 'auto'] };
});

const getYColumns = createSelector([getAgricultureSubsectorsData], data => {
  if (!data || !data.length) return null;
  const yColumns = data
    .map(({ emission_subcategory: { name } }) => ({
      label: name,
      value: getYColumnValue(name)
    }))
    .filter(y => y !== 'x');
  const yUniqColumns = uniqBy(yColumns, 'value');
  return yUniqColumns;
});

let colorThemeCache = {};

const getChartConfig = createSelector(
  [getChartData, getYColumns],
  (data, yColumns) => {
    if (!data || !yColumns) return null;

    const yColumnsChecked = yColumns;
    const chartColors = setChartColors(
      yColumnsChecked.length,
      CHART_COLORS,
      CHART_COLORS_EXTENDED
    );
    const theme = getThemeConfig(yColumnsChecked, chartColors);
    colorThemeCache = { ...theme, ...colorThemeCache };
    const tooltip = getTooltipConfig(yColumnsChecked);
    return {
      axes: {
        ...DEFAULT_AXES_CONFIG,
        yLeft: { ...DEFAULT_AXES_CONFIG.yLeft, unit: 'MtCO2e' }
      },
      theme: colorThemeCache,
      tooltip,
      columns: {
        x: [{ label: 'emission', value: 'x' }],
        y: yColumns
      }
    };
  }
);

const getFilterOptions = createSelector([getYColumns], yColumns => {
  if (!yColumns) return null;
  return yColumns.map(column => ({ ...column, groupId: 'subSectors' }));
});

/** PIE-CHART SELECTORS */
const getGhgEmissionsFilter = createSelector(
  [getEmissionCountrySelected, getGhgEmissionSource, getGhgEmissionGas],
  (selectedCountry, source, gas) => {
    if (!selectedCountry || !source || !gas) return null;
    return {
      location: selectedCountry.value,
      source: source.value,
      gas: gas.value
    };
  }
);

export const getPieChartData = createSelector([getGhgEmissionsData], data => {
  if (!data || !data.length) return null;

  const sectorsLastYearEmission = data
    .map(({ emissions, sector, location }) => {
      const lastYearEmission = emissions[emissions.length - 1];
      return { sector, location, ...lastYearEmission };
    })
    .filter(({ value }) => value > 0); // filter for negative emission for Forestry sector

  if (!sectorsLastYearEmission) return null;
  const totalEmission = sectorsLastYearEmission.reduce(
    (acc, emission) => acc + emission.value,
    0
  );

  const sectorEmissions = sectorsLastYearEmission.map(({ sector, value }) => ({
    name: getColumnValue(sector).toLowerCase(),
    value,
    sector,
    formattedValue: `${format('.2s')(value)}`,
    formattedPercentage: `${format('.2f')(value * 100 / totalEmission)}%`,
    percentageValue: value * 100 / totalEmission
  }));

  const agricultureRow = sectorEmissions.find(
    ({ name }) => name === 'agriculture'
  );
  const sectorsEmissionsData = agricultureRow
    ? [
      agricultureRow,
      ...sectorEmissions.filter(({ name }) => name !== 'agriculture')
    ]
    : sectorEmissions;
  const { location, year } = sectorsLastYearEmission[0];

  return {
    year,
    location,
    emissionValue: agricultureRow && agricultureRow.formattedValue,
    emissionPercentage: agricultureRow && agricultureRow.formattedPercentage,
    data: sectorsEmissionsData
  };
});

const getPieChartConfig = createSelector([getPieChartData], pieChartData => {
  if (!pieChartData || !pieChartData.data || !pieChartData.data.length) {
    return null;
  }
  const { data } = pieChartData;
  const columns = data.map(({ sector }) => ({
    label: sector,
    value: getColumnValue(sector).toLowerCase()
  }));

  const tooltip = getTooltipConfig(columns);
  let theme = getPieChartThemeConfig(columns, GREY_CHART_COLORS);
  const agricultureTheme = {
    ...theme.agriculture,
    stroke: AGRICULTURE_COLOR
  };
  theme = { ...theme, agriculture: agricultureTheme };

  const config = {
    tooltip,
    animation: true,
    theme,
    innerRadius: 50,
    outerRadius: 90,
    hideLabel: true,
    hideLegend: true
  };

  return config;
});

const getPieChartPayload = createSelector(
  [getPieChartData, getPieChartConfig, getGhgEmissionsLoading],
  (pieChartData, config, loading) => {
    if (!pieChartData || !config) return null;

    const {
      location,
      year,
      emissionValue,
      emissionPercentage,
      data
    } = pieChartData;
    const color = AGRICULTURE_COLOR;

    return {
      location,
      year: `${year}`,
      emissionValue,
      emissionPercentage,
      data,
      config,
      color,
      loading
    };
  }
);

// TO DO
// const getButtonGroupConfig = createSelector();

export const getAllData = createStructuredSelector({
  data: getChartData,
  loading: getAgricultureEmissionsLoading,
  config: getChartConfig,
  domain: getChartDomain,
  filters: getFilterOptions,
  locations: getLocationsOptions,
  emissionsCountry: getEmissionCountrySelected,
  ghgEmissionsFilters: getGhgEmissionsFilter,
  pieChartData: getPieChartPayload
});
<|endoftext|>


<commit_before>/*
**  BindingJS -- View Data Binding for JavaScript <http://bindingjs.com>
**  Copyright (c) 2014 Ralf S. Engelschall <http://engelschall.com>
**
**  This Source Code Form is subject to the terms of the Mozilla Public
**  License (MPL), version 2.0. If a copy of the MPL was not distributed
**  with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/* global module: true */
module.exports = function (grunt) {
    /*  stage 1: linting  */
    grunt.config.merge({
        newer: {
            "stage1-src": {
                src: [ "src/core/*.js", "src/plugin/*.js" ],
                dest: "bld/stage1/done-jshint-src",
                options: { tasks: [ "jshint:stage1-src", "touch:stage1-src" ] }
            },
            "stage1-tst": {
                src: [ "tst/**/*.js" ],
                dest: "bld/stage1/done-jshint-tst",
                options: { tasks: [ "jshint:stage1-tst", "touch:stage1-tst" ] }
            }
        },
        jshint: {
            "stage1-src": [
                "src/core/*.js",
                "src/plugin/*.js"
            ],
            "stage1-tst": [
                "tst/**/*.js"
            ]
        },
        touch: {
            "stage1-src": {
                src: [ "bld/stage1/done-jshint-src" ]
            },
            "stage1-tst": {
                src: [ "bld/stage1/done-jshint-tst" ]
            }
        },
        clean: {
            "stage1-src": [
                "bld/stage1/done-jshint-src"
            ],
            "stage1-tst": [
                "bld/stage1/done-jshint-tst"
            ]
        }
    })
    grunt.registerTask("stage1", [
        "newer:stage1-src",
        "newer:stage1-tst"
    ])
    grunt.registerTask("stage1-clean", [
        "clean:stage1-src",
        "clean:stage1-tst"
    ])

    /*  stage 2: compiling & transpiling  */
    grunt.config.merge({
        newer: {
            "stage2-src": {
                src: [ "src/**/*.js", "!src/umd/*.js" ],
                dest: "bld/stage2/src/core/binding.js",
                options: { tasks: [ "es6transpiler:stage2-src" ] }
            },
            "stage2-src-peg": {
                src: [ "src/core/binding-3-dsl-2-grammar.pegjs" ],
                dest: "bld/stage2/src/core/binding-3-dsl-2-grammar.js",
                options: { tasks: [ "shell:stage2-src-peg" ] }
            },
            "stage2-src-umd": {
                src: [ "src/umd/*.js" ],
                dest: "bld/stage2/src/umd/umd-library-prolog.js",
                options: { tasks: [ "copy:stage2-src-umd" ] }
            },
            "stage2-tst": {
                src: [ "tst/**/*.js" ],
                dest: "bld/stage2/tst/common.js",
                options: { tasks: [ "es6transpiler:stage2-tst" ] }
            }
        },
        es6transpiler: {
            options: {
                environments: [ "browser" ],
                disallowVars: false,
                disallowDuplicated: false,
                disallowUnknownReferences: false,
                includePolyfills: false
            },
            "stage2-src": {
                files: [{
                    expand: true,
                    flatten: false,
                    cwd: "src",
                    src: [ "**/*.js", "!umd/*.js" ],
                    dest: "bld/stage2/src/"
                }]
            },
            "stage2-tst": {
                files: [{
                    expand: true,
                    flatten: false,
                    cwd: "tst",
                    src: "**/*.js",
                    dest: "bld/stage2/tst/"
                }]
            }
        },
        peg: {
            "stage2-src-peg": {
                src:  "src/core/binding-3-dsl-2-grammar.pegjs",
                dest: "bld/stage2/src/core/binding-3-dsl-2-grammar.js",
                options: {
                    exportVar: "module.exports",
                    allowedStartRules: [ "spec", "binding" ],
                    optimize: "speed",
                    cache: true
                }
            }
        },
        shell: {
            "stage2-src-peg": {
                command: "pegjs " +
                    "--export-var module.exports " +
                    "--allowed-start-rules spec,binding " +
                    "--optimize speed " +
                    "--cache " +
                    "src/core/binding-3-dsl-2-grammar.pegjs " +
                    "bld/stage2/src/core/binding-3-dsl-2-grammar.js",
                options: {
                    stdout: true,
                    stderr: true
                }
            }
        },
        copy: {
            "stage2-src-umd": {
                files: [{
                    expand: true,
                    src: [ "src/umd/*.js" ],
                    dest: "bld/stage2/",
                    flatten: false
                }]
            }
        },
        clean: {
            "stage2-src": [
                "bld/stage2/src"
            ],
            "stage2-tst": [
                "bld/stage2/tst"
            ]
        }
    })
    grunt.registerTask("stage2", [
        "newer:stage2-src",
        "newer:stage2-src-peg",
        "newer:stage2-src-umd",
        "newer:stage2-tst"
    ])
    grunt.registerTask("stage2-clean", [
        "clean:stage3-src",
        "clean:stage2-tst"
    ])

    /*  stage 3: merge  */
    grunt.config.merge({
        newer: {
            "stage3-src-core": {
                src: [ "bld/stage2/src/core/*.js", "bld/stage2/src/umd/*.js" ],
                dest: "bld/stage3/src/core/binding.js",
                options: { tasks: [ "expand-include:stage3-src-core" ] }
            },
            "stage3-src-plugin": {
                src: [ "bld/stage2/src/plugin/*.js" ],
                dest: "bld/stage3/src/plugin/binding.plugin.componentjs.js",
                options: { tasks: [ "expand-include:stage3-src-plugin" ] }
            }
        },
        "expand-include": {
            "stage3-src-core": {
                src: "bld/stage2/src/core/binding.js",
                dest: "bld/stage3/src/core/binding.js",
                options: {
                    directiveSyntax: "js",
                    globalDefines: {
                        major: "<%= version.major %>",
                        minor: "<%= version.minor %>",
                        micro: "<%= version.micro %>",
                        date:  "<%= version.date  %>"
                    }
                }
            },
            "stage3-src-plugin": {
                files: [{
                    expand: true,
                    src: [ "bld/stage2/src/plugin/*.js" ],
                    dest: "bld/stage3/src/plugin/",
                    flatten: true
                }],
                options: {
                    directiveSyntax: "js"
                }
            }
        },
        clean: {
            "stage3-src": [
                "bld/stage3/src"
            ]
        }
    })
    grunt.registerTask("stage3", [
        "newer:stage3-src-core",
        "newer:stage3-src-plugin"
    ])
    grunt.registerTask("stage3-clean", [
        "clean:stage3-src"
    ])

    /*  stage 4: minification  */
    grunt.config.merge({
        newer: {
            "stage4": {
                src: [ "bld/stage3/src/core/*.js", "bld/stage3/src/plugin/*.js" ],
                dest: "bld/stage4/binding.min.js",
                options: { tasks: [ "uglify:stage4" ] }
            }
        },
        uglify: {
            "stage4": {
                files: [{
                    expand: true,
                    src: [ "bld/stage3/**/*.js" ],
                    dest: "bld/stage4/",
                    rename: function (dest, src) { return dest + src.replace(/\.js$/, ".min.js") },
                    flatten: true
                }]
            },
            options: {
                preserveComments: "some",
                report: "none"
            }
        },
        clean: {
            "stage4": [
                "bld/stage4/src/**/*.min.js"
            ]
        }
    })
    grunt.registerTask("stage4", [
        "newer:stage4"
    ])
    grunt.registerTask("stage4-clean", [
        "clean:stage4"
    ])

    /*  task aliasing  */
    grunt.registerTask("src-build", [
        "stage1",
        "stage2",
        "stage3",
        "stage4"
    ])
    grunt.registerTask("src-clean", [
        "stage1-clean",
        "stage2-clean",
        "stage3-clean",
        "stage4-clean"
    ])
}

<commit_msg>ignore files with embedded code as JSHint dislikes it<commit_after>/*
**  BindingJS -- View Data Binding for JavaScript <http://bindingjs.com>
**  Copyright (c) 2014 Ralf S. Engelschall <http://engelschall.com>
**
**  This Source Code Form is subject to the terms of the Mozilla Public
**  License (MPL), version 2.0. If a copy of the MPL was not distributed
**  with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/* global module: true */
module.exports = function (grunt) {
    /*  stage 1: linting  */
    grunt.config.merge({
        newer: {
            "stage1-src": {
                src: [ "src/core/*.js", "src/plugin/*.js" ],
                dest: "bld/stage1/done-jshint-src",
                options: { tasks: [ "jshint:stage1-src", "touch:stage1-src" ] }
            },
            "stage1-tst": {
                src: [ "tst/**/*.js" ],
                dest: "bld/stage1/done-jshint-tst",
                options: { tasks: [ "jshint:stage1-tst", "touch:stage1-tst" ] }
            }
        },
        jshint: {
            "stage1-src": [
                "src/core/*.js",
                "!src/core/binding-2-util-2-promise.js",
                "!src/core/binding-2-util-3-ducky.js",
                "src/plugin/*.js"
            ],
            "stage1-tst": [
                "tst/**/*.js"
            ]
        },
        touch: {
            "stage1-src": {
                src: [ "bld/stage1/done-jshint-src" ]
            },
            "stage1-tst": {
                src: [ "bld/stage1/done-jshint-tst" ]
            }
        },
        clean: {
            "stage1-src": [
                "bld/stage1/done-jshint-src"
            ],
            "stage1-tst": [
                "bld/stage1/done-jshint-tst"
            ]
        }
    })
    grunt.registerTask("stage1", [
        "newer:stage1-src",
        "newer:stage1-tst"
    ])
    grunt.registerTask("stage1-clean", [
        "clean:stage1-src",
        "clean:stage1-tst"
    ])

    /*  stage 2: compiling & transpiling  */
    grunt.config.merge({
        newer: {
            "stage2-src": {
                src: [ "src/**/*.js", "!src/umd/*.js" ],
                dest: "bld/stage2/src/core/binding.js",
                options: { tasks: [ "es6transpiler:stage2-src" ] }
            },
            "stage2-src-peg": {
                src: [ "src/core/binding-3-dsl-2-grammar.pegjs" ],
                dest: "bld/stage2/src/core/binding-3-dsl-2-grammar.js",
                options: { tasks: [ "shell:stage2-src-peg" ] }
            },
            "stage2-src-umd": {
                src: [ "src/umd/*.js" ],
                dest: "bld/stage2/src/umd/umd-library-prolog.js",
                options: { tasks: [ "copy:stage2-src-umd" ] }
            },
            "stage2-tst": {
                src: [ "tst/**/*.js" ],
                dest: "bld/stage2/tst/common.js",
                options: { tasks: [ "es6transpiler:stage2-tst" ] }
            }
        },
        es6transpiler: {
            options: {
                environments: [ "browser" ],
                disallowVars: false,
                disallowDuplicated: false,
                disallowUnknownReferences: false,
                includePolyfills: false
            },
            "stage2-src": {
                files: [{
                    expand: true,
                    flatten: false,
                    cwd: "src",
                    src: [ "**/*.js", "!umd/*.js" ],
                    dest: "bld/stage2/src/"
                }]
            },
            "stage2-tst": {
                files: [{
                    expand: true,
                    flatten: false,
                    cwd: "tst",
                    src: "**/*.js",
                    dest: "bld/stage2/tst/"
                }]
            }
        },
        peg: {
            "stage2-src-peg": {
                src:  "src/core/binding-3-dsl-2-grammar.pegjs",
                dest: "bld/stage2/src/core/binding-3-dsl-2-grammar.js",
                options: {
                    exportVar: "module.exports",
                    allowedStartRules: [ "spec", "binding" ],
                    optimize: "speed",
                    cache: true
                }
            }
        },
        shell: {
            "stage2-src-peg": {
                command: "pegjs " +
                    "--export-var module.exports " +
                    "--allowed-start-rules spec,binding " +
                    "--optimize speed " +
                    "--cache " +
                    "src/core/binding-3-dsl-2-grammar.pegjs " +
                    "bld/stage2/src/core/binding-3-dsl-2-grammar.js",
                options: {
                    stdout: true,
                    stderr: true
                }
            }
        },
        copy: {
            "stage2-src-umd": {
                files: [{
                    expand: true,
                    src: [ "src/umd/*.js" ],
                    dest: "bld/stage2/",
                    flatten: false
                }]
            }
        },
        clean: {
            "stage2-src": [
                "bld/stage2/src"
            ],
            "stage2-tst": [
                "bld/stage2/tst"
            ]
        }
    })
    grunt.registerTask("stage2", [
        "newer:stage2-src",
        "newer:stage2-src-peg",
        "newer:stage2-src-umd",
        "newer:stage2-tst"
    ])
    grunt.registerTask("stage2-clean", [
        "clean:stage3-src",
        "clean:stage2-tst"
    ])

    /*  stage 3: merge  */
    grunt.config.merge({
        newer: {
            "stage3-src-core": {
                src: [ "bld/stage2/src/core/*.js", "bld/stage2/src/umd/*.js" ],
                dest: "bld/stage3/src/core/binding.js",
                options: { tasks: [ "expand-include:stage3-src-core" ] }
            },
            "stage3-src-plugin": {
                src: [ "bld/stage2/src/plugin/*.js" ],
                dest: "bld/stage3/src/plugin/binding.plugin.componentjs.js",
                options: { tasks: [ "expand-include:stage3-src-plugin" ] }
            }
        },
        "expand-include": {
            "stage3-src-core": {
                src: "bld/stage2/src/core/binding.js",
                dest: "bld/stage3/src/core/binding.js",
                options: {
                    directiveSyntax: "js",
                    globalDefines: {
                        major: "<%= version.major %>",
                        minor: "<%= version.minor %>",
                        micro: "<%= version.micro %>",
                        date:  "<%= version.date  %>"
                    }
                }
            },
            "stage3-src-plugin": {
                files: [{
                    expand: true,
                    src: [ "bld/stage2/src/plugin/*.js" ],
                    dest: "bld/stage3/src/plugin/",
                    flatten: true
                }],
                options: {
                    directiveSyntax: "js"
                }
            }
        },
        clean: {
            "stage3-src": [
                "bld/stage3/src"
            ]
        }
    })
    grunt.registerTask("stage3", [
        "newer:stage3-src-core",
        "newer:stage3-src-plugin"
    ])
    grunt.registerTask("stage3-clean", [
        "clean:stage3-src"
    ])

    /*  stage 4: minification  */
    grunt.config.merge({
        newer: {
            "stage4": {
                src: [ "bld/stage3/src/core/*.js", "bld/stage3/src/plugin/*.js" ],
                dest: "bld/stage4/binding.min.js",
                options: { tasks: [ "uglify:stage4" ] }
            }
        },
        uglify: {
            "stage4": {
                files: [{
                    expand: true,
                    src: [ "bld/stage3/**/*.js" ],
                    dest: "bld/stage4/",
                    rename: function (dest, src) { return dest + src.replace(/\.js$/, ".min.js") },
                    flatten: true
                }]
            },
            options: {
                preserveComments: "some",
                report: "none"
            }
        },
        clean: {
            "stage4": [
                "bld/stage4/src/**/*.min.js"
            ]
        }
    })
    grunt.registerTask("stage4", [
        "newer:stage4"
    ])
    grunt.registerTask("stage4-clean", [
        "clean:stage4"
    ])

    /*  task aliasing  */
    grunt.registerTask("src-build", [
        "stage1",
        "stage2",
        "stage3",
        "stage4"
    ])
    grunt.registerTask("src-clean", [
        "stage1-clean",
        "stage2-clean",
        "stage3-clean",
        "stage4-clean"
    ])
}

<|endoftext|>