<commit_before>// v2 schema
'use strict';

const Joi = require('joi');
const config = require('config');
const dataDict = require(config.get('v2.dataDict'));

let descriptions = {};

for (let table in dataDict) {
    const cols = dataDict[table];
    for (let i = 0, j = cols.length; i < j; i++) {
        descriptions[cols[i].plazi] = cols[i].definition;
    }
}

module.exports = {

    communities: {
        query: {
            name: Joi.string()
                .description('The Zenodo Community to be queried for the records; defaults to "biosyslit"')
                .valid('all', 'biosyslit', 'belgiumherbarium')
                .default('biosyslit')
                .required(),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .default(false),
        }
    },

    images: {
        query: {

            id: Joi.number()
                .description("record id. All other query params are ignored if id is provided.")
                .integer()
                .positive()
                .optional(),

            // If 'id' is present in the queryString, all of
            // the below are ignored if also present.
            // The following rules apply *only* if 'id' is
            // not present
            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('id', {
                    is: Joi.number().integer().positive(),
                    then: Joi.optional(),
                    otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('id', {
                    is: Joi.number().integer().positive(),
                    then: Joi.optional(),
                    otherwise: Joi.required()
                }),

            // communities: Joi.string()
            //     .description('The community on Zenodo; defaults to "biosyslit"')
            //     .valid('all', 'BLR', 'belgiumherbarium')
            //     .default('BLR')
            //     .optional(),

            q: Joi.string()
                .description('any text string')
                .optional(),

            file_type: Joi.string()
                .description('File type, usually determined by the extension')
                .optional()
                .valid(
                    'png',
                    'jpg',
                    'pdf',
                    'xml',
                    'xlsx',
                    'docx',
                    'xls',
                    'csv',
                    'svg',
                    'doc'
                ),

            type: Joi.string()
                .description('Type of resource')
                .optional()
                .valid(
                    'image',
                    'publication',
                    'dataset',
                    'presentation',
                    'video'
                ),

            image_subtype: Joi.string()
                .description('Subtype based on the file_type \"image\"')
                .optional()
                .when(
                    'type', {
                        is: 'image',
                        then: Joi.valid(
                            'figure',
                            'photo',
                            'drawing',
                            'other',
                            'diagram',
                            'plot'
                        )
                    }
                ),

            publication_subtype: Joi.string()
                .description('Subtype based on the file_type \"publication\"')
                .optional()
                .when(
                    'type', {
                        is: 'image',
                        then: Joi.valid(
                            'article',
                            'conferencepaper',
                            'report',
                            'other',
                            'book',
                            'thesis',
                            'section',
                            'workingpaper',
                            'deliverable',
                            'preprint'
                        )
                    }
                ),

            access_right: Joi.string()
                .description('Access rights for the resource')
                .optional()
                .valid(
                    'open',
                    'closed',
                    'embargoed',
                    'restricted'
                ),

            keywords: Joi.array()
                .description('More than one keywords may be used')
                .when('id', {is: Joi.number().integer().positive(), then: Joi.optional() } )
                .optional(),

            // summary: Joi.boolean()
            //     .description('Summarize the results to record IDs')
            //     .default(true),

            // images: Joi.boolean()
            //     .description('Return only image links for each record'),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false)
        }
    },

    publications: {
        query: {

            id: Joi.number()
                .description("record id. All other query params are ignored if id is provided.")
                .integer()
                .positive()
                .optional(),

            // If 'id' is present in the queryString, all of
            // the below are ignored if also present.
            // The following rules apply *only* if 'id' is
            // not present
            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('id', {
                    is: Joi.number().integer().positive(),
                    then: Joi.optional(),
                    otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('id', {
                    is: Joi.number().integer().positive(),
                    then: Joi.optional(),
                    otherwise: Joi.required()
                }),

            // communities: Joi.string()
            //     .description('The community on Zenodo; defaults to "biosyslit"')
            //     .valid('all', 'BLR', 'belgiumherbarium')
            //     .default('BLR')
            //     .optional(),

            q: Joi.string()
                .description('any text string')
                .optional(),

            file_type: Joi.string()
                .description('File type, usually determined by the extension')
                .optional()
                .valid(
                    'png',
                    'jpg',
                    'pdf',
                    'xml',
                    'xlsx',
                    'docx',
                    'xls',
                    'csv',
                    'svg',
                    'doc'
                ),

            type: Joi.string()
                .description('Type of resource')
                .optional()
                .valid(
                    'image',
                    'publication',
                    'dataset',
                    'presentation',
                    'video'
                ),

            image_subtype: Joi.string()
                .description('Subtype based on the file_type \"image\"')
                .optional()
                .when(
                    'type', {
                        is: 'image',
                        then: Joi.valid(
                            'figure',
                            'photo',
                            'drawing',
                            'other',
                            'diagram',
                            'plot'
                        )
                    }
                ),

            publication_subtype: Joi.string()
                .description('Subtype based on the file_type \"publication\"')
                .optional()
                .when(
                    'type', {
                        is: 'image',
                        then: Joi.valid(
                            'article',
                            'conferencepaper',
                            'report',
                            'other',
                            'book',
                            'thesis',
                            'section',
                            'workingpaper',
                            'deliverable',
                            'preprint'
                        )
                    }
                ),

            access_right: Joi.string()
                .description('Access rights for the resource')
                .optional()
                .valid(
                    'open',
                    'closed',
                    'embargoed',
                    'restricted'
                ),

            keywords: Joi.array()
                .description('More than one keywords may be used')
                .when('id', {is: Joi.number().integer().positive(), then: Joi.optional() } )
                .optional(),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false)
        }
    },

    files: {
        params: {
            file_id: Joi.string()
        }
    },

    treatments: {
        query: {

            treatmentId: Joi.string()
                .description(descriptions.treatmentId)
                .optional(),

            format: Joi.string()
                .description('Response format')
                .when('treatmentId', {
                    is: Joi.string(),
                    then: Joi.optional()
                }),

            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('treatmentId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('treatmentId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false),

            // If 'treatmentId' is present in the queryString, all of the below are
            // ignored if also present. The following rules apply *only* if
            // 'treatmentId' is not present
            treatmentTitle: Joi.string()
                .description(descriptions.treatmentTitle)
                .optional(),

            journalTitle: Joi.string()
                .description(descriptions.journalTitle)
                .optional(),

            journalYear: Joi.string()
                .description(descriptions.journalYear)
                .optional(),

            authorityName: Joi.string()
                .description(descriptions.authorityName)
                .optional(),

            authorityYear: Joi.string()
                .description(descriptions.authorityYear)
                .optional(),

            kingdom: Joi.string()
                .description(descriptions.kingdom)
                .optional(),

            phylum: Joi.string()
                .description(descriptions.phylum)
                .optional(),

            order: Joi.string()
                .description(descriptions['"order"'])
                .optional(),

            family: Joi.string()
                .description(descriptions.family)
                .optional(),

            genus: Joi.string()
                .description(descriptions.genus)
                .optional(),

            species: Joi.string()
                .description(descriptions.species)
                .optional(),

            rank: Joi.string()
                .description(descriptions.rank)
                .optional(),

            q: Joi.string()
                .description(descriptions.fullText)
                .optional(),

            lat: Joi.number()
                .min(-180)
                .max(180)
                .description(descriptions.latitude)
                .optional(),

            lon: Joi.number()
                .min(-180)
                .max(180)
                .description(descriptions.longitude)
                .optional()
        }
    },

    figureCitations: {
        query: {
            figureCitationId: Joi.string()
                .description(descriptions.figureCitationId)
                .optional(),

            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('figureCitationId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('figureCitationId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false),

            q: Joi.string()
                .description(descriptions.fullText)
                .optional()
        }
    },

    bibRefCitations: {
        query: {
            bibRefCitationId: Joi.string()
                .description(descriptions.bibRefCitationId)
                .optional(),

            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('bibRefCitationId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('bibRefCitationId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false),

            q: Joi.string()
                .description(descriptions.fullText)
                .optional()
        }
    },

    treatmentAuthors: {
        query: {
            treatmentAuthorId: Joi.string()
                .description(descriptions.treatmentAuthorId)
                .optional(),

            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('treatmentAuthorId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('treatmentAuthorId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false),

            q: Joi.string()
                .description(descriptions.fullText)
                .optional()
        }
    },

    wpsummary: {
        query: {
            q: Joi.string().required()
        }
    },

    // no caching for any of the resources below
    authors: {
        query: {
            q: Joi.string().required()
        }
    },

    keywords: {
        query: {
            q: Joi.string().required()
        }
    },

    families: {
        query: {
            q: Joi.string().required()
        }
    },

    taxa: {
        query: {
            q: Joi.string().required()
        }
    }

}
<commit_msg>tweaked publication schema<commit_after>// v2 schema
'use strict';

const Joi = require('joi');
const config = require('config');
const dataDict = require(config.get('v2.dataDict'));

let descriptions = {};

for (let table in dataDict) {
    const cols = dataDict[table];
    for (let i = 0, j = cols.length; i < j; i++) {
        descriptions[cols[i].plazi] = cols[i].definition;
    }
}

module.exports = {

    communities: {
        query: {
            name: Joi.string()
                .description('The Zenodo Community to be queried for the records; defaults to "biosyslit"')
                .valid('all', 'biosyslit', 'belgiumherbarium')
                .default('biosyslit')
                .required(),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .default(false),
        }
    },

    images: {
        query: {

            id: Joi.number()
                .description("record id. All other query params are ignored if id is provided.")
                .integer()
                .positive()
                .optional(),

            // If 'id' is present in the queryString, all of
            // the below are ignored if also present.
            // The following rules apply *only* if 'id' is
            // not present
            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('id', {
                    is: Joi.number().integer().positive(),
                    then: Joi.optional(),
                    otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('id', {
                    is: Joi.number().integer().positive(),
                    then: Joi.optional(),
                    otherwise: Joi.required()
                }),

            // communities: Joi.string()
            //     .description('The community on Zenodo; defaults to "biosyslit"')
            //     .valid('all', 'BLR', 'belgiumherbarium')
            //     .default('BLR')
            //     .optional(),

            q: Joi.string()
                .description('any text string')
                .optional(),

            file_type: Joi.string()
                .description('File type, usually determined by the extension')
                .optional()
                .valid(
                    'png',
                    'jpg',
                    'pdf',
                    'xml',
                    'xlsx',
                    'docx',
                    'xls',
                    'csv',
                    'svg',
                    'doc'
                ),

            type: Joi.string()
                .description('Type of resource')
                .optional()
                .valid(
                    'image',
                    'publication',
                    'dataset',
                    'presentation',
                    'video'
                ),

            image_subtype: Joi.string()
                .description('Subtype based on the file_type \"image\"')
                .optional()
                .when(
                    'type', {
                        is: 'image',
                        then: Joi.valid(
                            'figure',
                            'photo',
                            'drawing',
                            'other',
                            'diagram',
                            'plot'
                        )
                    }
                ),

            subtype: Joi.string()
                .description('Subtype based on the file_type \"publication\"')
                .optional()
                .when(
                    'type', {
                        is: 'image',
                        then: Joi.valid(
                            'article',
                            'conferencepaper',
                            'report',
                            'other',
                            'book',
                            'thesis',
                            'section',
                            'workingpaper',
                            'deliverable',
                            'preprint'
                        )
                    }
                ),

            access_right: Joi.string()
                .description('Access rights for the resource')
                .optional()
                .valid(
                    'open',
                    'closed',
                    'embargoed',
                    'restricted'
                ),

            keywords: Joi.array()
                .description('More than one keywords may be used')
                .when('id', {is: Joi.number().integer().positive(), then: Joi.optional() } )
                .optional(),

            // summary: Joi.boolean()
            //     .description('Summarize the results to record IDs')
            //     .default(true),

            // images: Joi.boolean()
            //     .description('Return only image links for each record'),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false)
        }
    },

    publications: {
        query: {

            id: Joi.number()
                .description("record id. All other query params are ignored if id is provided.")
                .integer()
                .positive()
                .optional(),

            // If 'id' is present in the queryString, all of
            // the below are ignored if also present.
            // The following rules apply *only* if 'id' is
            // not present
            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('id', {
                    is: Joi.number().integer().positive(),
                    then: Joi.optional(),
                    otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('id', {
                    is: Joi.number().integer().positive(),
                    then: Joi.optional(),
                    otherwise: Joi.required()
                }),

            // communities: Joi.string()
            //     .description('The community on Zenodo; defaults to "biosyslit"')
            //     .valid('all', 'BLR', 'belgiumherbarium')
            //     .default('BLR')
            //     .optional(),

            q: Joi.string()
                .description('any text string')
                .optional(),

            file_type: Joi.string()
                .description('File type, usually determined by the extension')
                .optional()
                .valid(
                    'png',
                    'jpg',
                    'pdf',
                    'xml',
                    'xlsx',
                    'docx',
                    'xls',
                    'csv',
                    'svg',
                    'doc'
                ),

            type: Joi.string()
                .description('Type of resource')
                .optional()
                .valid(
                    'image',
                    'publication',
                    'dataset',
                    'presentation',
                    'video'
                ),

            image_subtype: Joi.string()
                .description('Subtype based on the file_type \"image\"')
                .optional()
                .when(
                    'type', {
                        is: 'image',
                        then: Joi.valid(
                            'figure',
                            'photo',
                            'drawing',
                            'other',
                            'diagram',
                            'plot'
                        )
                    }
                ),

            subtype: Joi.string()
                .description('Subtype based on the file_type \"publication\"')
                .optional()
                .when(
                    'type', {
                        is: 'publication',
                        then: Joi.valid(
                            'article',
                            'taxonomictreatment',
                            'report',
                            'other',
                            'book',
                            'thesis',
                            'section',
                            'workingpaper',
                            'deliverable',
                            'preprint'
                        )
                    }
                ),

            access_right: Joi.string()
                .description('Access rights for the resource')
                .optional()
                .valid(
                    'open',
                    'closed',
                    'embargoed',
                    'restricted'
                ),

            keywords: Joi.array()
                .description('More than one keywords may be used')
                .when('id', {is: Joi.number().integer().positive(), then: Joi.optional() } )
                .optional(),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false)
        }
    },

    files: {
        params: {
            file_id: Joi.string()
        }
    },

    treatments: {
        query: {

            treatmentId: Joi.string()
                .description(descriptions.treatmentId)
                .optional(),

            format: Joi.string()
                .description('Response format')
                .when('treatmentId', {
                    is: Joi.string(),
                    then: Joi.optional()
                }),

            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('treatmentId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('treatmentId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false),

            // If 'treatmentId' is present in the queryString, all of the below are
            // ignored if also present. The following rules apply *only* if
            // 'treatmentId' is not present
            treatmentTitle: Joi.string()
                .description(descriptions.treatmentTitle)
                .optional(),

            journalTitle: Joi.string()
                .description(descriptions.journalTitle)
                .optional(),

            journalYear: Joi.string()
                .description(descriptions.journalYear)
                .optional(),

            authorityName: Joi.string()
                .description(descriptions.authorityName)
                .optional(),

            authorityYear: Joi.string()
                .description(descriptions.authorityYear)
                .optional(),

            kingdom: Joi.string()
                .description(descriptions.kingdom)
                .optional(),

            phylum: Joi.string()
                .description(descriptions.phylum)
                .optional(),

            order: Joi.string()
                .description(descriptions['"order"'])
                .optional(),

            family: Joi.string()
                .description(descriptions.family)
                .optional(),

            genus: Joi.string()
                .description(descriptions.genus)
                .optional(),

            species: Joi.string()
                .description(descriptions.species)
                .optional(),

            rank: Joi.string()
                .description(descriptions.rank)
                .optional(),

            q: Joi.string()
                .description(descriptions.fullText)
                .optional(),

            lat: Joi.number()
                .min(-180)
                .max(180)
                .description(descriptions.latitude)
                .optional(),

            lon: Joi.number()
                .min(-180)
                .max(180)
                .description(descriptions.longitude)
                .optional()
        }
    },

    figureCitations: {
        query: {
            figureCitationId: Joi.string()
                .description(descriptions.figureCitationId)
                .optional(),

            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('figureCitationId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('figureCitationId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false),

            q: Joi.string()
                .description(descriptions.fullText)
                .optional()
        }
    },

    bibRefCitations: {
        query: {
            bibRefCitationId: Joi.string()
                .description(descriptions.bibRefCitationId)
                .optional(),

            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('bibRefCitationId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('bibRefCitationId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false),

            q: Joi.string()
                .description(descriptions.fullText)
                .optional()
        }
    },

    treatmentAuthors: {
        query: {
            treatmentAuthorId: Joi.string()
                .description(descriptions.treatmentAuthorId)
                .optional(),

            page: Joi.number()
                .integer()
                .description('Starting page, defaults to 1')
                .default(1)
                .when('treatmentAuthorId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            size: Joi.number()
                .integer()
                .description('Number of records to fetch per query, defaults to 30')
                .default(30)
                .when('treatmentAuthorId', {
                    is: Joi.string(),
                    then: Joi.optional(),
                    //otherwise: Joi.required()
                }),

            refreshCache: Joi.boolean()
                .description("force refresh cache")
                .optional()
                .default(false),

            q: Joi.string()
                .description(descriptions.fullText)
                .optional()
        }
    },

    wpsummary: {
        query: {
            q: Joi.string().required()
        }
    },

    // no caching for any of the resources below
    authors: {
        query: {
            q: Joi.string().required()
        }
    },

    keywords: {
        query: {
            q: Joi.string().required()
        }
    },

    families: {
        query: {
            q: Joi.string().required()
        }
    },

    taxa: {
        query: {
            q: Joi.string().required()
        }
    }

}
<|endoftext|>


<commit_before>require('babel/register');

const fs = require('fs');
const path = require('path');
const args = require('yargs').argv;

const name = args._[0];

const config = require('../../config');

const connect = !!args.connect;

if (typeof name !== 'string' || !name.match(/^[A-Z]([A-Z]|[a-z])+$/)) {
  console.error('Missing valid view name.' +
    ' Ensure name starts with a capital letter and is a string,' +
    ' e.g. node createView.js MyView');
  process.exit(9);
}

const dir = path.resolve(__dirname, '__templates__');

new Promise((resolve, reject) => {
  fs.readdir(dir, (err, files) => err ? reject(err) : resolve(files))
}).then((result) => {
  return new Promise((resolve) => {
    var fileList = [], total = result.length;
    result.forEach((filename) => {
      fs.readFile(path.resolve(dir, filename), 'utf-8', (err, data) => {
        if (!connect && /ViewConnect__js/.test(filename) || connect && /View__js/.test(filename)) {
          console.log(filename, connect);
          return total--;
        }
        fileList.push({fileName: filename, fileData: err || data});
        if (fileList.length === total) {
          resolve(fileList);
        }
      });
    });
  })
}).then((result) => {
  function resolveFileName(fileName) {
    if (fileName.match(/^package/)) {
      return 'package.json';
    }
    return name + '.' + (fileName.replace(/__/g, '.').replace(/^.+?\.(.+)\.txt$/, '$1'));
  }
  return new Promise((resolve, reject) => {
    result.forEach((data) => {
      var fileData = data.fileData.replace(/\$\{NAME\}/g, name);
      var fileName = resolveFileName(data.fileName);
      var filePath = path.resolve(config.get('utils_paths').src('views'), name, fileName);
      var length = result.length;
      var directory = path.dirname(filePath);
      if (!fs.existsSync(path.dirname(filePath))) {
        fs.mkdirSync(path.dirname(filePath));
      }
      fs.writeFile(filePath, fileData, (err) => {
        if (err) {
          reject(err)
        } else if (!--length) {
          resolve();
        }
      });
    })
  })
}).catch((err) => {
  console.log(err);
});
<commit_msg>Update createView script to include new view as export in views index.js<commit_after>require('babel/register');

//
// Modules and config
// -----------------------------------------------------------------------------

const fs = require('fs');
const path = require('path');
const args = require('yargs').argv;

const name = args._[0];

const config = require('../../config');
const paths = config.get('utils_paths');

const connect = !!args.connect;

//
// Argument validation
// -----------------------------------------------------------------------------

if (typeof name !== 'string' || !name.match(/^[A-Z]([A-Z]|[a-z])+$/)) {
  console.error('Missing valid view name.' +
    ' Ensure name starts with a capital letter and is a string,' +
    ' e.g. node createView.js MyView');
  process.exit(9);
}

//
// Core functionality
// -----------------------------------------------------------------------------

const dir = path.resolve(__dirname, '__templates__');

new Promise((resolve, reject) => {
  fs.readdir(dir, (err, files) => err ? reject(err) : resolve(files))
}).then((result) => {
  return new Promise((resolve) => {
    var fileList = [], total = result.length;
    result.forEach((filename) => {
      fs.readFile(path.resolve(dir, filename), 'utf-8', (err, data) => {
        if (!connect && /ViewConnect__js/.test(filename) || connect && /View__js/.test(filename)) {
          return total--;
        }
        fileList.push({fileName: filename, fileData: err || data});
        if (fileList.length === total) {
          resolve(fileList);
        }
      });
    });
  })
}).then((result) => {
  function resolveFileName(fileName) {
    if (fileName.match(/^package/)) {
      return 'package.json';
    }
    return name + '.' + (fileName.replace(/__/g, '.').replace(/^.+?\.(.+)\.txt$/, '$1'));
  }
  return new Promise((resolve, reject) => {
    var total = result.length;
    result.forEach((data) => {
      var fileData = data.fileData.replace(/\$\{NAME\}/g, name);
      var fileName = resolveFileName(data.fileName);
      var filePath = path.resolve(paths.src('views'), name, fileName);
      if (!fs.existsSync(path.dirname(filePath))) {
        fs.mkdirSync(path.dirname(filePath));
      }
      fs.writeFile(filePath, fileData, (err) => {
        if (err) {
          reject(err)
        } else if (!--total) {
          resolve();
        }
      });
    })
  })
}).catch((err) => {
  console.log(err);
  process.exit(0);
}).then(() => {
  return new Promise((resolve, reject) => {
    const file = paths.src('views/index.js');
    fs.readFile(file, 'utf8', (err, data) => {
      return err ? reject(err) : resolve({file, data});
    })
  });
}).then((result) => {
  fs.writeFileSync(result.file, `export {default as ${name + 'View'}} from './${name}';\n${result.data}`);
}).catch((err) => {
  console.error(err);
});
<|endoftext|>


<commit_before>$(window).load(function(){
  // Place for us to stash debug info for JS console access.
  window.sensors = {};

  // ---------------------------------------------------------------------------
  // Utility functions
  // ---------------------------------------------------------------------------
  function log(msg) {
    try{
      console.log(msg);
    } catch (e) {
      // Browser doesn't support console.
    }
  }

  // ---------------------------------------------------------------------------
  // Startup.
  // ---------------------------------------------------------------------------
  function drawChart() {

    jQuery.getJSON("/readings", null, function(data) {
      log("Downloaded " + data["readings"].length + " readings");
      window.sensors["data"] = data;
      var chartData = new google.visualization.DataTable();
      chartData.addColumn('datetime', 'Date');
      chartData.addColumn('number', 'Temperature/C');

      var readings = data["readings"];
      for (var i = 0; i < readings.length; i++) {
        readings[i][0] = new Date(readings[i][0]*1000);
      }

      chartData.addRows(readings);

      // Set chart options
      var options = {'title':'Temperature',
                     'width': 900,
                     'height':600,
                     'hAxis.format': 'MMM d HH:mm'};

      // Instantiate and draw our chart, passing in some options.
      var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
      chart.draw(chartData, options);
    })
    .error(function() {
      log("Failed to load readings");
    });
  }

  drawChart();
});
<commit_msg>Correct scale<commit_after>$(window).load(function(){
  // Place for us to stash debug info for JS console access.
  window.sensors = {};

  // ---------------------------------------------------------------------------
  // Utility functions
  // ---------------------------------------------------------------------------
  function log(msg) {
    try{
      console.log(msg);
    } catch (e) {
      // Browser doesn't support console.
    }
  }

  // ---------------------------------------------------------------------------
  // Startup.
  // ---------------------------------------------------------------------------
  function drawChart() {

    jQuery.getJSON("/readings", null, function(data) {
      log("Downloaded " + data["readings"].length + " readings");
      window.sensors["data"] = data;
      var chartData = new google.visualization.DataTable();
      chartData.addColumn('datetime', 'Date');
      chartData.addColumn('number', 'Temperature/C');

      var readings = data["readings"];
      for (var i = 0; i < readings.length; i++) {
        readings[i][0] = new Date(readings[i][0]*1000);
      }

      chartData.addRows(readings);

      // Set chart options
      var options = {
          'title':'Temperature',
          'width': 900,
          'height':600,
          'hAxis': {'format': 'MMM d HH:mm'}
      };

      // Instantiate and draw our chart, passing in some options.
      var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
      chart.draw(chartData, options);
    })
    .error(function() {
      log("Failed to load readings");
    });
  }

  drawChart();
});
<|endoftext|>


<commit_before>import { Store } from 'consus-core/flux';
import StudentStore from './student-store';
import { checkOutItems } from '../lib/api-client';

let items = [];

let timer = null;

function clearTimer() {
    clearTimeout(timer);
    timer = null;
}

class CartStore extends Store {
    getItems() {
        return items;
    }

    isOnTimeout(){
        return timer !== null;
    }
}

const store = new CartStore();

store.TIMEOUT_TIME = 60000;

store.registerHandler('STUDENT_FOUND', () => {
    if(store.isOnTimeout()){
        clearTimer();
    }
});

store.registerHandler('CHECKOUT_ITEM_FOUND', data => {
    if(store.isOnTimeout()){
        clearTimer();
    }
    let item = {
        address: data.address,
        status: data.status
    };
    items.push(item);
    timer = setTimeout(() => {
        checkOutItems(StudentStore.getStudent().id, items.map(item => item.address));
        timer = null;
    }, store.TIMEOUT_TIME);
    store.emitChange();
});

store.registerHandler('CHECKOUT_SUCCESS', () => {
    clearTimer();
    store.waitFor(StudentStore);
    items = [];
    store.emitChange();
});

store.registerHandler('CLEAR_ALL_DATA', () => {
    items = [];
    store.emitChange();
});

store.registerHandler('CLEAR_ITEMS', () => {
    items = [];
    store.emitChange();
});

export default store;
<commit_msg>Fixed the timeout problem<commit_after>import { Store } from 'consus-core/flux';
import StudentStore from './student-store';
import { checkOutItems } from '../lib/api-client';
import StudentController from '../controllers/pages/student';

let items = [];

let timer = null;

function clearTimer() {
    clearTimeout(timer);
    timer = null;
}

class CartStore extends Store {
    getItems() {
        return items;
    }

    isOnTimeout(){
        return timer !== null;
    }
}

const store = new CartStore();

store.TIMEOUT_TIME = 60000;

store.registerHandler('STUDENT_FOUND', () => {
    if(store.isOnTimeout()){
        clearTimer();
    }
});

store.registerHandler('CHECKOUT_ITEM_FOUND', data => {
    if(store.isOnTimeout()){
        clearTimer();
    }
    let item = {
        address: data.address,
        status: data.status
    };
    items.push(item);
    timer = setTimeout(() => {
        StudentController.checkout(StudentStore.getStudent().id, items.map(item => item.address));
        timer = null;
    }, store.TIMEOUT_TIME);
    store.emitChange();
});

store.registerHandler('CHECKOUT_SUCCESS', () => {
    clearTimer();
    store.waitFor(StudentStore);
    items = [];
    store.emitChange();
});

store.registerHandler('CLEAR_ALL_DATA', () => {
    items = [];
    store.emitChange();
});

store.registerHandler('CLEAR_ITEMS', () => {
    items = [];
    store.emitChange();
});

export default store;
<|endoftext|>


<commit_before>import * as ActionTypes from '../constants/actionTypes';

import MockDate from 'mockdate';
import { createStore } from 'redux';

import calculator from '../utils/fuelSavingsCalculator';
import {getFormattedDateTime} from '../utils/dateHelper';
import initialState from '../reducers/initialState';
import rootReducer from '../reducers';

describe('Store', () => {
  let dateModified;
  beforeAll(() => {
    MockDate.set(new Date());
    dateModified = getFormattedDateTime();
  });
  afterAll(() => MockDate.reset());

  it('should display results when necessary data is provided', () => {
    const store = createStore(rootReducer, initialState);

    const actions = [
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'month' }
    ];
    actions.forEach(action => store.dispatch(action));

    const actual = store.getState();
    const expected = {
      newMpg: 20,
      tradeMpg: 10,
      newPpg: 1.50,
      tradePpg: 1.50,
      milesDriven: 100,
      milesDrivenTimeframe: 'month',
      displayResults: false,
      dateModified,
      necessaryDataIsProvidedToCalculateSavings: true,
      savings: calculator().calculateSavings(store.getState().fuelSavings)
    };

    expect(actual.fuelSavings).toEqual(expected);
  });

  it('should not display results when necessary data is not provided', () => {
    const store = createStore(rootReducer, initialState);

    const actions = [
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      // { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'month' }
    ];

    actions.forEach(action => store.dispatch(action));

    const actual = store.getState();

    const expected = {
      newMpg: 20,
      tradeMpg: 10,
      newPpg: '',
      tradePpg: 1.5,
      milesDriven: 100,
      milesDrivenTimeframe: 'month',
      displayResults: false,
      dateModified,
      necessaryDataIsProvidedToCalculateSavings: false,
      savings: { annual: 0, monthly: 0, threeYear: 0 }
    };

    expect(actual.fuelSavings).toEqual(expected);
  });

  it('should handle a flurry of actions', () => {
    const store = createStore(rootReducer, initialState);

    const actions = [
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'month' },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 },
      { type: ActionTypes.SAVE_FUEL_SAVINGS, dateModified, settings: store.getState() },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'week' },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 }
    ];
    actions.forEach(action => store.dispatch(action));

    const lastGoodSavings = calculator().calculateSavings(store.getState().fuelSavings);

    const moreActions = [
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 0 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'year' }
    ];

    moreActions.forEach(action => store.dispatch(action));

    const actual = store.getState();
    //const expected = {
    //  newMpg: 20,
    //  tradeMpg: 10,
    //  newPpg: 1.50,
    //  tradePpg: 0,
    //  milesDriven: 100,
    //  milesDrivenTimeframe: 'year',
    //  displayResults: false,
    //  dateModified,
    //  necessaryDataIsProvidedToCalculateSavings: false,
    //  savings: lastGoodSavings
    //};
    //
    //expect(actual.fuelSavings).toEqual(expected);

    // with jest snapshots the above assertion can be replaced with this one line
    // jest will store the value in a file within ./__snapshots__
    // snapshots can/should be committed and reviewed
    // jest will also update snapshot or delete unused ones using the command `npm run test -- -u`
    expect(actual.fuelSavings).toMatchSnapshot();
  });
});
<commit_msg>updated tests to use static mockModifiedDate<commit_after>import * as ActionTypes from '../constants/actionTypes';

import MockDate from 'mockdate';
import { createStore } from 'redux';

import calculator from '../utils/fuelSavingsCalculator';
import {getFormattedDateTime} from '../utils/dateHelper';
import initialState from '../reducers/initialState';
import rootReducer from '../reducers';

describe('Store', () => {
  let dateModified;
  beforeAll(() => {
    // hardcoded date for consistency in tests and snapshots on all machines
    MockDate.set(new Date("1/31 23:14:01"));
    dateModified = getFormattedDateTime();
  });
  afterAll(() => MockDate.reset());

  it('should display results when necessary data is provided', () => {
    const store = createStore(rootReducer, initialState);

    const actions = [
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'month' }
    ];
    actions.forEach(action => store.dispatch(action));

    const actual = store.getState();
    const expected = {
      newMpg: 20,
      tradeMpg: 10,
      newPpg: 1.50,
      tradePpg: 1.50,
      milesDriven: 100,
      milesDrivenTimeframe: 'month',
      displayResults: false,
      dateModified,
      necessaryDataIsProvidedToCalculateSavings: true,
      savings: calculator().calculateSavings(store.getState().fuelSavings)
    };

    expect(actual.fuelSavings).toEqual(expected);
  });

  it('should not display results when necessary data is not provided', () => {
    const store = createStore(rootReducer, initialState);

    const actions = [
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      // { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'month' }
    ];

    actions.forEach(action => store.dispatch(action));

    const actual = store.getState();

    const expected = {
      newMpg: 20,
      tradeMpg: 10,
      newPpg: '',
      tradePpg: 1.5,
      milesDriven: 100,
      milesDrivenTimeframe: 'month',
      displayResults: false,
      dateModified,
      necessaryDataIsProvidedToCalculateSavings: false,
      savings: { annual: 0, monthly: 0, threeYear: 0 }
    };

    expect(actual.fuelSavings).toEqual(expected);
  });

  it('should handle a flurry of actions', () => {
    const store = createStore(rootReducer, initialState);

    const actions = [
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'month' },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 },
      { type: ActionTypes.SAVE_FUEL_SAVINGS, dateModified, settings: store.getState() },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 1.50 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'week' },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newMpg', value: 20 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradeMpg', value: 10 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'newPpg', value: 1.50 }
    ];
    actions.forEach(action => store.dispatch(action));

    const lastGoodSavings = calculator().calculateSavings(store.getState().fuelSavings);

    const moreActions = [
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'tradePpg', value: 0 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDriven', value: 100 },
      { type: ActionTypes.CALCULATE_FUEL_SAVINGS, dateModified, settings: store.getState(), fieldName: 'milesDrivenTimeframe', value: 'year' }
    ];

    moreActions.forEach(action => store.dispatch(action));

    const actual = store.getState();
    //const expected = {
    //  newMpg: 20,
    //  tradeMpg: 10,
    //  newPpg: 1.50,
    //  tradePpg: 0,
    //  milesDriven: 100,
    //  milesDrivenTimeframe: 'year',
    //  displayResults: false,
    //  dateModified,
    //  necessaryDataIsProvidedToCalculateSavings: false,
    //  savings: lastGoodSavings
    //};
    //
    //expect(actual.fuelSavings).toEqual(expected);

    // with jest snapshots the above assertion can be replaced with this one line
    // jest will store the value in a file within ./__snapshots__
    // snapshots can/should be committed and reviewed
    // jest will also update snapshot or delete unused ones using the command `npm run test -- -u`
    expect(actual.fuelSavings).toMatchSnapshot();
  });
});
<|endoftext|>


<commit_before>var DEFAULT_CHANGE_HANDLER = 'onChange',
  React = require('react')

var StoreMixin = {
  contextTypes: {
    getStore: React.PropTypes.func.isRequired
  },

  /**
   * Registers staticly declared listeners
   * @method componentDidMount
   */
  componentDidMount: function componentDidMount () {
    this.listeners = []

    // Register static listeners
    this.getListeners().forEach(listener => {
       this._attachStoreListener(listener)
    })
  },

  /**
   * Gets a store instance from the context
   * @param {Function|String} store The store to get
   * @returns {Object}
   * @method getStore
   */
  getStore: function getStore (store) {
    if (!this.context.getStore)
      throw new Error('getStore was called but was not found in the context')
    return typeof store !== 'object' ? this.context.getStore(store) : store
  },

  /**
   * Gets from the context all store instances required by this component
   * @returns {Object}
   * @method getStores
   */
  getStores: function getStores () {
    var storesByName = this.getListeners().reduce((accum, listener) => {
      accum[listener.store.constructor.storeName] = listener.store
      return accum
    }, {})
    return Object.keys(storesByName).map(storeName => storesByName[storeName])
  },

  /**
   * Gets a store-change handler from the component
   * @param {Function|String} handler The handler to get
   * @returns {Function}
   * @method getHandler
   */
  getHandler: function (handler) {
    if (typeof handler === 'string')
      handler = this[handler]

    if (!handler)
      throw new Error('storeListener attempted to add undefined handler')

    return handler
  },

  /**
   * Gets a listener descriptor for a store and store-change handler
   * @param {Function|String} store Store
   * @param {Function|String} handler The handler function or method name
   * @returns {Object}
   * @method getListener
   */
  getListener: function (store, handler) {
    return {
      store: this.getStore(store),
      handler: this.getHandler(handler)
    }
  },

  /**
   * Gets all store-change listener descriptors from the component
   * @returns {Object}
   * @method getListeners
   */
  getListeners: function () {
    var storeListeners = this.constructor.storeListeners // Static property on component

    if (!storeListeners)
      return []

    if (Array.isArray(storeListeners))
      return storeListeners.map(store => this.getListener(store, DEFAULT_CHANGE_HANDLER))

    return Object.keys(storeListeners).reduce((accum, handler) => {
      var stores = storeListeners[handler]
      if (!Array.isArray(stores))
        stores = [stores]
      return accum.concat(stores.map(store => this.getListener(store, handler)))
    }, [])

  },

  /**
   * If provided with events, will attach listeners to events on EventEmitter objects(i.e. Stores)
   * If the component isn't mounted, events aren't attached.
   * @param {Object} listener
   * @param {Object} listener.store Store instance
   * @param {Object} listener.handler Handler function or method name
   * @method _attachStoreListener
   * @private
   */
  _attachStoreListener: function _attachStoreListener (listener) {
    if (this.isMounted && !this.isMounted())
      throw new Error(`storeListener mixin called listen when component wasn't mounted`)

    listener.store.addChangeListener(listener.handler)
    this.listeners.push(listener)
  },

  /**
   * Removes all listeners
   * @method componentWillUnmount
   */
  componentWillUnmount: function componentWillUnmount () {
    this.listeners.forEach((listener) => {
      listener.store.removeChangeListener(listener.handler)
    })
    this.listeners = []
  }

}

module.exports = StoreMixin
<commit_msg>switch property order for readability<commit_after>var DEFAULT_CHANGE_HANDLER = 'onChange',
  React = require('react')

var StoreMixin = {
  contextTypes: {
    getStore: React.PropTypes.func.isRequired
  },

  /**
   * Registers staticly declared listeners
   * @method componentDidMount
   */
  componentDidMount: function componentDidMount () {
    this.listeners = []

    // Register static listeners
    this.getListeners().forEach(listener => {
       this._attachStoreListener(listener)
    })
  },

  /**
   * Gets a store instance from the context
   * @param {Function|String} store The store to get
   * @returns {Object}
   * @method getStore
   */
  getStore: function getStore (store) {
    if (!this.context.getStore)
      throw new Error('getStore was called but was not found in the context')
    return typeof store !== 'object' ? this.context.getStore(store) : store
  },

  /**
   * Gets from the context all store instances required by this component
   * @returns {Object}
   * @method getStores
   */
  getStores: function getStores () {
    var storesByName = this.getListeners().reduce((accum, listener) => {
      accum[listener.store.constructor.storeName] = listener.store
      return accum
    }, {})
    return Object.keys(storesByName).map(storeName => storesByName[storeName])
  },

  /**
   * Gets a store-change handler from the component
   * @param {Function|String} handler The handler to get
   * @returns {Function}
   * @method getHandler
   */
  getHandler: function (handler) {
    if (typeof handler === 'string')
      handler = this[handler]

    if (!handler)
      throw new Error('storeListener attempted to add undefined handler')

    return handler
  },

  /**
   * Gets a listener descriptor for a store and store-change handler
   * @param {Function|String} store Store
   * @param {Function|String} handler The handler function or method name
   * @returns {Object}
   * @method getListener
   */
  getListener: function (store, handler) {
    return {
      handler: this.getHandler(handler),
      store: this.getStore(store)
    }
  },

  /**
   * Gets all store-change listener descriptors from the component
   * @returns {Object}
   * @method getListeners
   */
  getListeners: function () {
    var storeListeners = this.constructor.storeListeners // Static property on component

    if (!storeListeners)
      return []

    if (Array.isArray(storeListeners))
      return storeListeners.map(store => this.getListener(store, DEFAULT_CHANGE_HANDLER))

    return Object.keys(storeListeners).reduce((accum, handler) => {
      var stores = storeListeners[handler]
      if (!Array.isArray(stores))
        stores = [stores]
      return accum.concat(stores.map(store => this.getListener(store, handler)))
    }, [])

  },

  /**
   * If provided with events, will attach listeners to events on EventEmitter objects(i.e. Stores)
   * If the component isn't mounted, events aren't attached.
   * @param {Object} listener
   * @param {Object} listener.store Store instance
   * @param {Object} listener.handler Handler function or method name
   * @method _attachStoreListener
   * @private
   */
  _attachStoreListener: function _attachStoreListener (listener) {
    if (this.isMounted && !this.isMounted())
      throw new Error(`storeListener mixin called listen when component wasn't mounted`)

    listener.store.addChangeListener(listener.handler)
    this.listeners.push(listener)
  },

  /**
   * Removes all listeners
   * @method componentWillUnmount
   */
  componentWillUnmount: function componentWillUnmount () {
    this.listeners.forEach((listener) => {
      listener.store.removeChangeListener(listener.handler)
    })
    this.listeners = []
  }

}

module.exports = StoreMixin
<|endoftext|>


<commit_before>angular.module('app.services', [])

.factory('Game', ['$q', '$http', 'store', 'socket', '$timeout', 'ionicToast', '$rootScope', function($q, $http, store, socket, $timeout, ionicToast, $rootScope){

  var obj = {
    submitting: false,

    started: false,

    response: '',

    topic: '',

    isReader: false,

    isCreator: false,

    submitting_response: false,

    submitting_topic: false,

    submitting_guess: false,

    game: {

      id: undefined,

      max_score: undefined,

      winner: undefined,

      completed: false,

      skip_if_guessed: undefined,

      guesser: undefined,

      //Array of objects, with id, name, guessed, and score
      players: [],

      //Object containing all pertinent info for the round
      current_round: {

        reader_name: undefined,

        reader_id: undefined,

        //All answers in, ready to start guessing
        ready: false,

        //Array of objects, with id, text, guessed, and user_id
        responses: [

        ],

        //Current round's topic
        topic: ''
      },

      //Array of rounds from the server
      rounds: []
    },

    // The guess a user is going to make
    guess: {
      user: undefined,
      response: undefined
    },

    checkGame: function () {
      var remote_id = store.get('remote_id');
      if (!remote_id) {
        return $q.resolve();
      }
      return $http({
        url: Config.api + '/users/' + remote_id,
        method: 'get'
      })
      .then(function (response) {
        var current_game_id = response.data.user.current_game_id;
        store.set('current_game_id', current_game_id);
        obj.game.id = current_game_id;
        return !!current_game_id;
      })
      .catch(function (error) {
        console.log('check game error', error);
      })
    },

    getGame: function () {
      obj.game.id = store.get('current_game_id');
      if (!obj.game.id) {
        obj.resetGame();
        return $q.resolve();
      }
      return $http({
        url: Config.api + '/games/' + obj.game.id,
        method: 'get'
      })
      .then(function (response) {
        var results = response.data.results;
        obj.game.players = results.players;
        obj.game.guesser = results.game.guesser;
        obj.game.winner = results.game.winner;
        var my_id = store.get('remote_id');
        for (var i = 0; i < obj.game.players.length; i++) {
          if (obj.game.players[i].id === my_id) {
            obj.isCreator = obj.game.players[i].creator ? true : false;
            break;
          }
        }
        obj.game.rounds = results.rounds;
        var lastRound = obj.game.rounds[0];
        if (lastRound && lastRound.reader_id === store.get('remote_id')) {
          obj.isReader = true;
        } else {
          obj.isReader = false;
        }
        obj.game.current_round = lastRound;
        if (obj.game.current_round && obj.game.current_round.responses.length === obj.game.players.length) {
          var randomizedResponses = [];
          for (var i = 0; i < obj.game.players.length; i++) {
            randomizedResponses.push(obj.game.current_round.responses.splice(Math.floor(Math.random() * obj.game.current_round.responses.length), 1)[0]);
          }
          obj.game.current_round.responses = JSON.parse(JSON.stringify(randomizedResponses));
          obj.game.current_round.ready = true;
        }
        obj.submitting_response = false;
        obj.submitting_topic = false;
        obj.submitting_guess = false;
        obj.game.completed = response.data.results.game.completed;
        obj.started = response.data.results.game.started;
        obj.game.id = response.data.results.game.id;
        obj.game.max_score = response.data.results.game.max_score;
        obj.game.skip_if_guessed = response.data.results.game.skip_if_guessed;
        socket.emit('room', obj.game.id);
      })
      .catch(function (error) {
        return error;
      })
    },

    resetGame: function () {
      obj.isReader = false;
      obj.started = false;
      obj.isCreator = false;
      obj.submitting_response = false;
      obj.submitting_topic = false;
      obj.submitting_guess = false;
      obj.game.players = [];
      obj.game.rounds = [];
      obj.game.topic = '';
      obj.game.id = undefined;
      obj.game.guesser = undefined;
      obj.game.response = '';
      obj.game.winner = undefined;
      obj.game.completed = false;
      obj.submitting = false;
      obj.game.currentRound  = {

        reader_name: undefined,

        reader_id: undefined,

        //All answers in, ready to start guessing
        ready: false,

        skip_if_guessed: undefined,

        //Array of objects, with id, text, guessed, and user_id
        responses: [

        ],

        //Current round's topic
        topic: ''
      };
      obj.game.rounds = [];
      obj.response = '';
      obj.current_round = {
        max_score: undefined,
        reader_id: undefined,
        reader_name: undefined,
        ready: false,
        response: []
      }
      obj.guess = {
        user: undefined,
        response: undefined
      }
    },

    updateGame: function  () {
      return obj.checkGame()
      .then(function (hasGame) {
        if (hasGame) {
          return obj.getGame();
        }
      })
      .catch(function (error) {
        console.log(error);
      });
    },

    startGame: function () {
      return $http({
        url: Config.api + '/games/' + obj.game.id + '/start',
        method: 'post'
      })
      .then(function (response) {
        if (response.data.started) {
          obj.started = true;
        }
      })
      .catch(function (error) {
        console.log('starting game error:', error);
      });
    },

    submitTopic: function () {
      obj.submitting_topic = true;
      var cacheTopic = obj.topic;
      return $http({
        url: Config.api + '/rounds/' + obj.game.current_round.id + '/topic',
        method: 'post',
        data: {topic: obj.topic}
      })
      .then(function (response) {
        if (response.data.submitted) {
          obj.topic = '';
          obj.game.current_round.topic = cacheTopic;
          ionicToast.show('Topic has been set!', 'top', false, 2500);
        } else {
          ionicToast.show('Error setting topic', 'top', false, 2500);
        }
      })
      .catch(function (error) {
        console.log('topic post error:', error);
        ionicToast.show('Error setting topic', 'top', false, 2500);
      })
      .finally(function () {
        obj.submitting_topic = false;
      });
    },

    submitResponse: function () {
      obj.submitting_response = true;
      var cacheResponse = obj.response;
      return $http({
        url: Config.api + '/rounds/' + obj.game.current_round.id + '/response',
        method: 'post',
        data: {
          text: obj.response,
          user_id: store.get('remote_id')
        }
      })
      .then(function (response) {
        if (response.data.submitted) {
          ionicToast.show('Response submitted!', 'top', false, 2500);
          obj.response = '';
        } else {
          ionicToast.show('There was an error...', 'top', false, 2500);
        }
      })
      .catch(function (error) {
        ionicToast.show('There was an error...', 'top', false, 2500);
        console.log('response error: ', error);
      })
      .finally(function () {
        obj.submitting_response = false;
      });
    },

    submitGuess: function (guessInfo) {
      obj.submitting_guess = true;
      guessInfo.guesser_id = store.get('remote_id');
      return $http({
        url: Config.api + '/rounds/' + obj.game.current_round.id + '/guess',
        method: 'POST',
        data: {
          guess: guessInfo
        }
      })
      .then( function (res) {
        obj.guess.user = undefined;
        obj.guess.response = undefined;
        return res;
      })
      .catch( function (error) {
        console.error(error);
      })
      .finally( function () {
        obj.submitting_guess = false;
      });
    },

    getPlayer: function (id) {
      var player;
      for (var i = 0; i < obj.game.players.length; i++) {
        if (obj.game.players[i].id === id) {
          player = obj.game.players[i];
        }
      }
      return player;
    },

    hasResponded: function () {
      if (!obj.game.current_round) {
        return false;
      }
      for (var i = 0; i < obj.game.current_round.responses.length; i++) {
        var response = obj.game.current_round.responses[i];
        if (response.user_id === store.get('remote_id')) {
          return true;
        }
      }
      return false;
    },

    revealResponses: function (index) {
      index = index || 0;
      if (index > obj.game.current_round.responses.length - 1) {
        return;
      }
      var response = obj.game.current_round.responses[index];
      if (response.revealed) {
        obj.revealResponses(++index);
      } else {
        responsiveVoice.speak(response.text, $rootScope.voice, {
          onend: function () {
            $http({
              'url': Config.api + '/responses/reveal',
              method: 'post',
              data: {
                game_id: obj.game.id,
                response_id: response.id
              }
            })
            .then(function (response) {
              if (response.status === 200) {
                $timeout(obj.revealResponses.bind(null, ++index), 675);
              }
            })
            .catch(function (error) {
              console.log('reveal error: ', error);
            });
          }
        })
      }
    },

    startReadingResponses: function () {
      responsiveVoice.speak('Here are the responses for this round. The topic is ' + obj.game.current_round.topic, $rootScope.voice,
        {
          onend: obj.revealResponses
        }
      );
    },

    amGuesser: function () {
      return obj.game.guesser && obj.game.guesser.id === store.get('remote_id');
    }
  }

  socket.on('invite response', function () {
    obj.getGame();
  });

  socket.on('start', function () {
    obj.started = true;
  });

  socket.on('round', function (round) {
    obj.game.rounds.unshift(round);
    if (round.reader_id === store.get('remote_id')) {
      obj.isReader = true;
    } else {
      obj.isReader = false;
    }
    obj.game.guesser = undefined;
    obj.game.current_round = round;
    obj.game.current_round.ready = false;
    obj.game.current_round.topic = '';
  });

  socket.on('topic', function (topic) {
    obj.game.current_round.topic = topic;
  });

  socket.on('response', function (response) {
    var match = undefined;
    if (obj.game.current_round.responses) {
      for (var i = 0; i < obj.game.current_round.responses.length; i++) {
        var checkResponse = obj.game.current_round.responses[i];
        if (response.id === checkResponse) {
          match = checkResponse;
          break;
        }
      }
    } else {
      obj.game.current_round.responses = [];
    }

    if (!match) {
      obj.game.current_round.responses.push(response);
    }

    if (obj.game.current_round && obj.game.current_round.responses.length === obj.game.players.length) {
      var randomizedResponses = [];
      for (var i = 0; i < obj.game.players.length; i++) {
        randomizedResponses.push(obj.game.current_round.responses.splice(Math.floor(Math.random() * obj.game.current_round.responses.length), 1)[0]);
      }
      obj.game.current_round.responses = JSON.parse(JSON.stringify(randomizedResponses));
      obj.game.current_round.ready = true;
      if (obj.isReader) {
        obj.startReadingResponses();
      }
    }
  });

  socket.on('guesser', function (guesser) {
    obj.response = '';
    obj.game.guesser = guesser;
  });

  socket.on('guess', function (guess) {
    var guessedResponse;
    var guessee;
    var guesser;
    for (var i = 0; i < obj.game.players.length; i++) {
      var player = obj.game.players[i];
      if (player.id === guess.details.guesser_id) {
        guesser = player;
      }
      if (player.id === guess.details.guessee_id) {
        guessee = player;
      }
      if (guess.result && guess.details.guessee_id === player.id) {
        obj.game.current_round.guesses = obj.game.current_round.guesses || {};
        obj.game.current_round.guesses[player.id] = true;
      }
    }
    for (var i = 0; i < obj.game.current_round.responses.length; i++) {
      var response = obj.game.current_round.responses[i];
      if (response.id === guess.details.response_id) {
        guessedResponse = response;
        if (guess.result) {
          response.guessed = true;
        }
        break;
      }
    }
    if (guess.result) {
      if (guess.newRound) {
        guesser.score = guesser.score + 2;
      } else {
        guesser.score = guesser.score + 1;
      }
    }
    var result = guess.result ? 'Correct!' : 'Wrong!'
    if (guess.won) {
      obj.game.winner = guesser;
      obj.game.completed = true;
      result = 'Game Over!'
    }
    var guess_message = guesser.full_name + ' guessed "' + guessedResponse.text + '" was written by ' + guessee.full_name + '. ' + result;
    ionicToast.show(guess_message, 'top', false, 2500);
  });

  socket.on('reveal', function (response_id) {
    for (var i = 0; i < obj.game.current_round.responses.length; i++) {
      if (obj.game.current_round.responses[i].id === response_id) {
        $timeout(function () {
          obj.game.current_round.responses[i].revealed = true;
        });
        break;
      }
    }
  });

  return obj;

}])

.factory('socket', function (socketFactory) {
  var io_socket = io(Config.api, {'forceNew': true});
  var socket = socketFactory({
    ioSocket: io_socket
  });
  return socket;
})

.factory('Auth', ['auth', 'store', '$state', 'socket', function(auth, store, $state, socket){

  var logout = function () {
    auth.signout();
    socket.emit('logout', store.get('remote_id'));
    store.remove('profile');
    store.remove('pic_url');
    store.remove('name');
    store.remove('token');
    store.remove('fb_access_token');
    store.remove('remote_id');
    $state.go('login');
  };

  return {
    logout: logout
  };

}])

.service('BlankService', [function(){

}]);
<commit_msg>(chore) even safer check to prevent an error<commit_after>angular.module('app.services', [])

.factory('Game', ['$q', '$http', 'store', 'socket', '$timeout', 'ionicToast', '$rootScope', function($q, $http, store, socket, $timeout, ionicToast, $rootScope){

  var obj = {
    submitting: false,

    started: false,

    response: '',

    topic: '',

    isReader: false,

    isCreator: false,

    submitting_response: false,

    submitting_topic: false,

    submitting_guess: false,

    game: {

      id: undefined,

      max_score: undefined,

      winner: undefined,

      completed: false,

      skip_if_guessed: undefined,

      guesser: undefined,

      //Array of objects, with id, name, guessed, and score
      players: [],

      //Object containing all pertinent info for the round
      current_round: {

        reader_name: undefined,

        reader_id: undefined,

        //All answers in, ready to start guessing
        ready: false,

        //Array of objects, with id, text, guessed, and user_id
        responses: [

        ],

        //Current round's topic
        topic: ''
      },

      //Array of rounds from the server
      rounds: []
    },

    // The guess a user is going to make
    guess: {
      user: undefined,
      response: undefined
    },

    checkGame: function () {
      var remote_id = store.get('remote_id');
      if (!remote_id) {
        return $q.resolve();
      }
      return $http({
        url: Config.api + '/users/' + remote_id,
        method: 'get'
      })
      .then(function (response) {
        var current_game_id = response.data.user.current_game_id;
        store.set('current_game_id', current_game_id);
        obj.game.id = current_game_id;
        return !!current_game_id;
      })
      .catch(function (error) {
        console.log('check game error', error);
      })
    },

    getGame: function () {
      obj.game.id = store.get('current_game_id');
      if (!obj.game.id) {
        obj.resetGame();
        return $q.resolve();
      }
      return $http({
        url: Config.api + '/games/' + obj.game.id,
        method: 'get'
      })
      .then(function (response) {
        var results = response.data.results;
        obj.game.players = results.players;
        obj.game.guesser = results.game.guesser;
        obj.game.winner = results.game.winner;
        var my_id = store.get('remote_id');
        for (var i = 0; i < obj.game.players.length; i++) {
          if (obj.game.players[i].id === my_id) {
            obj.isCreator = obj.game.players[i].creator ? true : false;
            break;
          }
        }
        obj.game.rounds = results.rounds;
        var lastRound = obj.game.rounds[0];
        if (lastRound && lastRound.reader_id === store.get('remote_id')) {
          obj.isReader = true;
        } else {
          obj.isReader = false;
        }
        obj.game.current_round = lastRound;
        if (obj.game.current_round && obj.game.current_round.responses.length === obj.game.players.length) {
          var randomizedResponses = [];
          for (var i = 0; i < obj.game.players.length; i++) {
            randomizedResponses.push(obj.game.current_round.responses.splice(Math.floor(Math.random() * obj.game.current_round.responses.length), 1)[0]);
          }
          obj.game.current_round.responses = JSON.parse(JSON.stringify(randomizedResponses));
          obj.game.current_round.ready = true;
        }
        obj.submitting_response = false;
        obj.submitting_topic = false;
        obj.submitting_guess = false;
        obj.game.completed = response.data.results.game.completed;
        obj.started = response.data.results.game.started;
        obj.game.id = response.data.results.game.id;
        obj.game.max_score = response.data.results.game.max_score;
        obj.game.skip_if_guessed = response.data.results.game.skip_if_guessed;
        socket.emit('room', obj.game.id);
      })
      .catch(function (error) {
        return error;
      })
    },

    resetGame: function () {
      obj.isReader = false;
      obj.started = false;
      obj.isCreator = false;
      obj.submitting_response = false;
      obj.submitting_topic = false;
      obj.submitting_guess = false;
      obj.game.players = [];
      obj.game.rounds = [];
      obj.game.topic = '';
      obj.game.id = undefined;
      obj.game.guesser = undefined;
      obj.game.response = '';
      obj.game.winner = undefined;
      obj.game.completed = false;
      obj.submitting = false;
      obj.game.currentRound  = {

        reader_name: undefined,

        reader_id: undefined,

        //All answers in, ready to start guessing
        ready: false,

        skip_if_guessed: undefined,

        //Array of objects, with id, text, guessed, and user_id
        responses: [

        ],

        //Current round's topic
        topic: ''
      };
      obj.game.rounds = [];
      obj.response = '';
      obj.current_round = {
        max_score: undefined,
        reader_id: undefined,
        reader_name: undefined,
        ready: false,
        response: []
      }
      obj.guess = {
        user: undefined,
        response: undefined
      }
    },

    updateGame: function  () {
      return obj.checkGame()
      .then(function (hasGame) {
        if (hasGame) {
          return obj.getGame();
        }
      })
      .catch(function (error) {
        console.log(error);
      });
    },

    startGame: function () {
      return $http({
        url: Config.api + '/games/' + obj.game.id + '/start',
        method: 'post'
      })
      .then(function (response) {
        if (response.data.started) {
          obj.started = true;
        }
      })
      .catch(function (error) {
        console.log('starting game error:', error);
      });
    },

    submitTopic: function () {
      obj.submitting_topic = true;
      var cacheTopic = obj.topic;
      return $http({
        url: Config.api + '/rounds/' + obj.game.current_round.id + '/topic',
        method: 'post',
        data: {topic: obj.topic}
      })
      .then(function (response) {
        if (response.data.submitted) {
          obj.topic = '';
          obj.game.current_round.topic = cacheTopic;
          ionicToast.show('Topic has been set!', 'top', false, 2500);
        } else {
          ionicToast.show('Error setting topic', 'top', false, 2500);
        }
      })
      .catch(function (error) {
        console.log('topic post error:', error);
        ionicToast.show('Error setting topic', 'top', false, 2500);
      })
      .finally(function () {
        obj.submitting_topic = false;
      });
    },

    submitResponse: function () {
      obj.submitting_response = true;
      var cacheResponse = obj.response;
      return $http({
        url: Config.api + '/rounds/' + obj.game.current_round.id + '/response',
        method: 'post',
        data: {
          text: obj.response,
          user_id: store.get('remote_id')
        }
      })
      .then(function (response) {
        if (response.data.submitted) {
          ionicToast.show('Response submitted!', 'top', false, 2500);
          obj.response = '';
        } else {
          ionicToast.show('There was an error...', 'top', false, 2500);
        }
      })
      .catch(function (error) {
        ionicToast.show('There was an error...', 'top', false, 2500);
        console.log('response error: ', error);
      })
      .finally(function () {
        obj.submitting_response = false;
      });
    },

    submitGuess: function (guessInfo) {
      obj.submitting_guess = true;
      guessInfo.guesser_id = store.get('remote_id');
      return $http({
        url: Config.api + '/rounds/' + obj.game.current_round.id + '/guess',
        method: 'POST',
        data: {
          guess: guessInfo
        }
      })
      .then( function (res) {
        obj.guess.user = undefined;
        obj.guess.response = undefined;
        return res;
      })
      .catch( function (error) {
        console.error(error);
      })
      .finally( function () {
        obj.submitting_guess = false;
      });
    },

    getPlayer: function (id) {
      var player;
      for (var i = 0; i < obj.game.players.length; i++) {
        if (obj.game.players[i].id === id) {
          player = obj.game.players[i];
        }
      }
      return player;
    },

    hasResponded: function () {
      if (!obj.game.current_round || !obj.game.current_round.responses) {
        return false;
      }
      for (var i = 0; i < obj.game.current_round.responses.length; i++) {
        var response = obj.game.current_round.responses[i];
        if (response.user_id === store.get('remote_id')) {
          return true;
        }
      }
      return false;
    },

    revealResponses: function (index) {
      index = index || 0;
      if (index > obj.game.current_round.responses.length - 1) {
        return;
      }
      var response = obj.game.current_round.responses[index];
      if (response.revealed) {
        obj.revealResponses(++index);
      } else {
        responsiveVoice.speak(response.text, $rootScope.voice, {
          onend: function () {
            $http({
              'url': Config.api + '/responses/reveal',
              method: 'post',
              data: {
                game_id: obj.game.id,
                response_id: response.id
              }
            })
            .then(function (response) {
              if (response.status === 200) {
                $timeout(obj.revealResponses.bind(null, ++index), 675);
              }
            })
            .catch(function (error) {
              console.log('reveal error: ', error);
            });
          }
        })
      }
    },

    startReadingResponses: function () {
      responsiveVoice.speak('Here are the responses for this round. The topic is ' + obj.game.current_round.topic, $rootScope.voice,
        {
          onend: obj.revealResponses
        }
      );
    },

    amGuesser: function () {
      return obj.game.guesser && obj.game.guesser.id === store.get('remote_id');
    }
  }

  socket.on('invite response', function () {
    obj.getGame();
  });

  socket.on('start', function () {
    obj.started = true;
  });

  socket.on('round', function (round) {
    obj.game.rounds.unshift(round);
    if (round.reader_id === store.get('remote_id')) {
      obj.isReader = true;
    } else {
      obj.isReader = false;
    }
    obj.game.guesser = undefined;
    obj.game.current_round = round;
    obj.game.current_round.ready = false;
    obj.game.current_round.topic = '';
  });

  socket.on('topic', function (topic) {
    obj.game.current_round.topic = topic;
  });

  socket.on('response', function (response) {
    var match = undefined;
    if (obj.game.current_round.responses) {
      for (var i = 0; i < obj.game.current_round.responses.length; i++) {
        var checkResponse = obj.game.current_round.responses[i];
        if (response.id === checkResponse) {
          match = checkResponse;
          break;
        }
      }
    } else {
      obj.game.current_round.responses = [];
    }

    if (!match) {
      obj.game.current_round.responses.push(response);
    }

    if (obj.game.current_round && obj.game.current_round.responses.length === obj.game.players.length) {
      var randomizedResponses = [];
      for (var i = 0; i < obj.game.players.length; i++) {
        randomizedResponses.push(obj.game.current_round.responses.splice(Math.floor(Math.random() * obj.game.current_round.responses.length), 1)[0]);
      }
      obj.game.current_round.responses = JSON.parse(JSON.stringify(randomizedResponses));
      obj.game.current_round.ready = true;
      if (obj.isReader) {
        obj.startReadingResponses();
      }
    }
  });

  socket.on('guesser', function (guesser) {
    obj.response = '';
    obj.game.guesser = guesser;
  });

  socket.on('guess', function (guess) {
    var guessedResponse;
    var guessee;
    var guesser;
    for (var i = 0; i < obj.game.players.length; i++) {
      var player = obj.game.players[i];
      if (player.id === guess.details.guesser_id) {
        guesser = player;
      }
      if (player.id === guess.details.guessee_id) {
        guessee = player;
      }
      if (guess.result && guess.details.guessee_id === player.id) {
        obj.game.current_round.guesses = obj.game.current_round.guesses || {};
        obj.game.current_round.guesses[player.id] = true;
      }
    }
    for (var i = 0; i < obj.game.current_round.responses.length; i++) {
      var response = obj.game.current_round.responses[i];
      if (response.id === guess.details.response_id) {
        guessedResponse = response;
        if (guess.result) {
          response.guessed = true;
        }
        break;
      }
    }
    if (guess.result) {
      if (guess.newRound) {
        guesser.score = guesser.score + 2;
      } else {
        guesser.score = guesser.score + 1;
      }
    }
    var result = guess.result ? 'Correct!' : 'Wrong!'
    if (guess.won) {
      obj.game.winner = guesser;
      obj.game.completed = true;
      result = 'Game Over!'
    }
    var guess_message = guesser.full_name + ' guessed "' + guessedResponse.text + '" was written by ' + guessee.full_name + '. ' + result;
    ionicToast.show(guess_message, 'top', false, 2500);
  });

  socket.on('reveal', function (response_id) {
    for (var i = 0; i < obj.game.current_round.responses.length; i++) {
      if (obj.game.current_round.responses[i].id === response_id) {
        $timeout(function () {
          obj.game.current_round.responses[i].revealed = true;
        });
        break;
      }
    }
  });

  return obj;

}])

.factory('socket', function (socketFactory) {
  var io_socket = io(Config.api, {'forceNew': true});
  var socket = socketFactory({
    ioSocket: io_socket
  });
  return socket;
})

.factory('Auth', ['auth', 'store', '$state', 'socket', function(auth, store, $state, socket){

  var logout = function () {
    auth.signout();
    socket.emit('logout', store.get('remote_id'));
    store.remove('profile');
    store.remove('pic_url');
    store.remove('name');
    store.remove('token');
    store.remove('fb_access_token');
    store.remove('remote_id');
    $state.go('login');
  };

  return {
    logout: logout
  };

}])

.service('BlankService', [function(){

}]);
<|endoftext|>


<commit_before>const input = require('fs').readFileSync('/dev/stdin', 'utf8')

const [_a, _b, _c] = input
  .trim()
  .split('\n')
const [a, b] = [_a, _b].map(x => parseInt(x))
const c = parseFloat(_c)

console.log(`NUMBER = ${parseInt(a)}`);
console.log(`SALARY = U$ ${(b * c).toFixed(2)}`);
<commit_msg>Fix lint<commit_after>const input = require('fs').readFileSync('/dev/stdin', 'utf8')

const [_a, _b, _c] = input
  .trim()
  .split('\n')
const [a, b] = [_a, _b].map(x => parseInt(x))
const c = parseFloat(_c)

console.log(`NUMBER = ${parseInt(a)}`)
console.log(`SALARY = U$ ${(b * c).toFixed(2)}`)
<|endoftext|>