<commit_before>var Scientilla = Scientilla || {};

Scientilla.user = {
    USER: 'user',
    ADMINISTRATOR: 'administrator',
    getAliases: function () {
        var firstLetter = function (string) {
            if (!string)
                return "";
            return string.charAt(0).toUpperCase();
        };
        var capitalize = function (string) {
            if (!string)
                return "";
            return _.capitalize(string.toLowerCase());
//                return str.replace(/\w\S*/g, function (txt) {
//                    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
//                });
        };
        var aliases = [];
        if (_.isEmpty(this.name) || _.isEmpty(this.surname))
            return aliases;
        var first_name = capitalize(this.name);
        var last_name = capitalize(this.surname);
        var initial_first_name = firstLetter(first_name);
        aliases.push(first_name + " " + last_name);
        aliases.push(last_name + " " + first_name);
        aliases.push(last_name + " " + initial_first_name + ".");
        aliases.push(initial_first_name + ". " + last_name + "");
        aliases = _.uniq(aliases);
        return aliases;
    },
    getUcAliases: function () {
        var aliases = this.getAliases();
        var ucAliases = _.map(aliases, function (a) {
            return a.toUpperCase();
        });
        return ucAliases;
    },
    getDisplayName: function () {
        var name = this.name ? this.name : "";
        var surname = this.surname ? this.surname : "";
        var fullName = _.trim(name + " " + surname);
        return fullName;
    },
    getType: function () {
        return 'user';
    },
    getCollaborationGroups: function () {
        return _.map(this.collaborations, 'group');
    },
    isAdmin: function () {
        return this.role === this.ADMINISTRATOR;
    },
    admins: function (group) {
        var administeredGroupsId = _.map(this.admininstratedGroups, 'id');
        return _.includes(administeredGroupsId, group.id);
    },
    //sTODO: move to a service
    getReferenceBrowsingUrl: function () {
        return '/users/' + this.id + '/references';
    },
    getExternalConnectors: function () {
        var connectors = [];
        var publicationsConnector = {name: 'Publications', enabled: true};
        var orcidConnector = {name: 'ORCID', enabled: !!this.orcidId};
        var scopusConnector = {name: 'Scopus', enabled: !!this.scopusId};
        connectors.push(publicationsConnector);
        connectors.push(orcidConnector);
        connectors.push(scopusConnector);
        return connectors;
    },
    getNewDocument: function (documentTypeObj) {
        return {
            title: "",
            authors: "",
            draftCreator: this.id,
            draft: true,
            type: documentTypeObj.key,
            sourceType: documentTypeObj.defaultSource,
            year: '',
            journal: '',
            issue: '',
            volume: '',
            pages: '',
            articleNumber: '',
            doi: '',
            bookTitle: '',
            editor: '',
            publisher: '',
            conferenceName: '',
            conferenceLocation: '',
            acronym: '',
            scopusId: '',
            wosId: ''
        };
    },
    getProfileUrl: function () {
        return '/users/' + this.id;
    }

};

//sTodo: evaluating wheter to reformat in a constructor style
Scientilla.reference = {
    UNKNOWN_REFERENCE: 0,
    USER_REFERENCE: 1,
    GROUP_REFERENCE: 2,
    DRAFT: 'draft',
    VERIFIED: 'verified',
    PUBLIC: 'public',
    create: function (referenceData) {
        var fields = [
            'authors',
            'title',
            'year',
            'journal',
            'issue',
            'volume',
            'pages',
            'articleNumber',
            'doi',
            'bookTitle',
            'editor',
            'publisher',
            'conferenceName',
            'conferenceLocation',
            'acronym',
            'type',
            'sourceType',
            'scopusId',
            'wosId'
        ];
        var reference = _.pick(referenceData, fields);
        _.extend(reference, Scientilla.reference);
        return reference;
    },
    getAllCoauthors: function () {
        return _.union(this.privateCoauthors, this.publicCoauthors);
    },
    getType: function () {
        if (!!this.owner)
            return this.USER_REFERENCE;
        else if (!!this.groupOwner)
            return this.GROUP_REFERENCE;
        else
            return this.UNKNOWN_REFERENCE;
    },
    getAuthors: function () {
        if (!this.authors)
            return [];

        return this.authors.replace(/\s+et all\s*$/i, '').split(',').map(_.trim);
    },
    getUcAuthors: function () {
        var authors = this.getAuthors();
        var ucAuthors = _.map(authors, function (a) {
            return a.toUpperCase();
        });
        return ucAuthors;
    },
    getDisplayName: function () {
        return this.getDisplayName();
    },
    getNewGroupReference: function (groupId) {
        return {
            title: "",
            authors: "",
            draftGroupCreator: groupId,
            draft: true
        };
    },
    getNewDraftReference: function (userId) {
        return {
            title: "",
            authors: "",
            draftCreator: userId,
            draft: true
        };
    },
    getDraftCreator: function () {
        if (this.draftCreator)
            return this.draftCreator;
        return this.draftGroupCreator;
    },
    getDocumentsFields: function (source) {
        var fields;
        if (source === 'journal')
            fields = {
                title: {
                    inputType: 'text'
                },
                authors: {
                    inputType: 'text'
                },
                year: {
                    inputType: 'text'
                },
                journal: {
                    inputType: 'text'
                },
                issue: {
                    inputType: 'text'
                },
                volume: {
                    inputType: 'text'
                },
                pages: {
                    inputType: 'text'
                },
                articleNumber: {
                    label: "Article number",
                    inputType: 'text'
                },
                doi: {
                    label: "DOI",
                    inputType: 'text'
                },
                scopusId: {
                    label: "Scopus ID",
                    inputType: 'text'
                },
                wosId: {
                    label: "WOS ID",
                    inputType: 'text'
                }
            };
        if (source === 'book')
            fields = {
                title: {
                    inputType: 'text'
                },
                bookTitle: {
                    label: 'Book title',
                    inputType: 'text'
                },
                authors: {
                    inputType: 'text'
                },
                year: {
                    inputType: 'text'
                },
                pages: {
                    inputType: 'text'
                },
                editor: {
                    inputType: 'text'
                },
                publisher: {
                    inputType: 'text'
                },
                scopusId: {
                    label: "Scopus ID",
                    inputType: 'text'
                },
                wosId: {
                    label: "WOS ID",
                    inputType: 'text'
                }
            };
        if (source === 'conference')
            fields = {
                title: {
                    inputType: 'text'
                },
                authors: {
                    inputType: 'text'
                },
                year: {
                    inputType: 'text'
                },
                conferenceName: {
                    label: 'Conference name',
                    inputType: 'text'
                },
                conferenceLocation: {
                    label: 'Conference location',
                    inputType: 'text'
                },
                acronym: {
                    inputType: 'text'
                },
                scopusId: {
                    label: "Scopus ID",
                    inputType: 'text'
                },
                wosId: {
                    label: "WOS ID",
                    inputType: 'text'
                }
            };
        if (!source) {
            fields = {
                title: {
                    inputType: 'text'
                },
                authors: {
                    inputType: 'text'
                },
                year: {
                    inputType: 'text'
                },
                scopusId: {
                    label: "Scopus ID",
                    inputType: 'text'
                },
                wosId: {
                    label: "WOS ID",
                    inputType: 'text'
                }
            };
        }
        return fields;
    },
    getDocumentTypes: function () {
        return [
            {
                key: 'article',
                label: 'Article',
                defaultSource: 'journal'
            },
            {
                key: 'article_in_press',
                label: 'Article in Press',
                defaultSource: 'journal'
            },
            {
                key: 'abstract_report',
                label: 'Abstract Report',
                defaultSource: null
            },
            {
                key: 'book',
                label: 'Book',
                defaultSource: 'book'
            },
            {
                key: 'book_chapter',
                label: 'Book Chapter',
                defaultSource: 'book'
            },
            {
                key: 'conference_paper',
                label: 'Conference Paper',
                defaultSource: 'conference'
            },
            {
                key: 'conference_review',
                label: 'Conference Review',
                defaultSource: 'conference'
            },
            {
                key: 'editorial',
                label: 'Editorial',
                defaultSource: null
            },
            {
                key: 'erratum',
                label: 'Erratum',
                defaultSource: null
            },
            {
                key: 'letter',
                label: 'Letter',
                defaultSource: null
            },
            {
                key: 'note',
                label: 'Note',
                defaultSource: null
            },
            {
                key: 'report',
                label: 'Report',
                defaultSource: null
            },
            {
                key: 'review',
                label: 'Review',
                defaultSource: 'journal'
            },
            {
                key: 'short_survey',
                label: 'Short Survey',
                defaultSource: null
            },
            {
                key: 'paper',
                label: 'Paper',
                defaultSource: 'conference'
            }
        ];
    },
    copyDocument: function (document, creator) {

        var excludedFields = ['draft', 'draftCreator', 'draftGroupCreator'];

        var documentTypeObj = {
            key: document.type,
            defaultSource: document.sourceType
        };

        var newDoc = creator.getNewDocument(documentTypeObj);

        _.forOwn(newDoc, function (value, key) {
            if (_.includes(excludedFields, key))
                return;

            newDoc[key] = document[key];
        });

        return newDoc;
    },
    isDiscarded: function() {
        return !!this.discarded;
    }
};

Scientilla.membership = {
    getDisplayName: function () {
        //sTODO: to be removed when deep populate is implemented
        if (_.isFunction(this.user.getDisplayName))
            return this.user.getDisplayName();
        else
            return '';
    }
};

Scientilla.collaboration = {
    getDisplayName: function () {
        //sTODO: to be removed when deep populate is implemented
        if (_.isFunction(this.group.getDisplayName))
            return this.group.getDisplayName();
        else
            return '';
    }
};

Scientilla.group = {
    getDisplayName: function () {
        return this.name;
    },
    getType: function () {
        return 'group';
    },
    //sTODO: move to a service
    getReferenceBrowsingUrl: function () {
        return '/groups/' + this.id + '/references';
    },
    getExternalConnectors: function () {
        var connectors = [];
        var publicationsConnector = {name: 'Publications', enabled: !!this.publicationsAcronym};
        var scopusConnector = {name: 'Scopus', enabled: !!this.scopusId};
        connectors.push(publicationsConnector);
        connectors.push(scopusConnector);
        return connectors;
    },
    getNewDocument: function (documentTypeObj) {
        return {
            title: "",
            authors: "",
            draftGroupCreator: this.id,
            draft: true,
            type: documentTypeObj.key,
            sourceType: documentTypeObj.defaultSource,
            year: '',
            journal: '',
            issue: '',
            volume: '',
            pages: '',
            articleNumber: '',
            doi: '',
            bookTitle: '',
            editor: '',
            publisher: '',
            conferenceName: '',
            conferenceLocation: '',
            acronym: '',
            scopusId: '',
            wosId: ''
        };
    },
    getProfileUrl: function () {
        return '/groups/' + this.id;
    }
};

Scientilla.toDocumentsCollection = function (documents) {
    _.each(documents, function (d) {
        _.assign(d, Scientilla.reference);
        Scientilla.toUsersCollection(d.privateCoauthors);
    });
};

Scientilla.toUsersCollection = function (users) {
    _.each(users, function (u) {
        _.assign(u, Scientilla.user);
    });
};
<commit_msg>Refactor.<commit_after>var Scientilla = Scientilla || {};

Scientilla.user = {
    USER: 'user',
    ADMINISTRATOR: 'administrator',
    getAliases: function () {
        var firstLetter = function (string) {
            if (!string)
                return "";
            return string.charAt(0).toUpperCase();
        };
        var capitalize = function (string) {
            if (!string)
                return "";
            return _.capitalize(string.toLowerCase());
//                return str.replace(/\w\S*/g, function (txt) {
//                    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
//                });
        };
        var aliases = [];
        if (_.isEmpty(this.name) || _.isEmpty(this.surname))
            return aliases;
        var first_name = capitalize(this.name);
        var last_name = capitalize(this.surname);
        var initial_first_name = firstLetter(first_name);
        aliases.push(first_name + " " + last_name);
        aliases.push(last_name + " " + first_name);
        aliases.push(last_name + " " + initial_first_name + ".");
        aliases.push(initial_first_name + ". " + last_name + "");
        aliases = _.uniq(aliases);
        return aliases;
    },
    getUcAliases: function () {
        var aliases = this.getAliases();
        var ucAliases = _.map(aliases, function (a) {
            return a.toUpperCase();
        });
        return ucAliases;
    },
    getDisplayName: function () {
        var name = this.name ? this.name : "";
        var surname = this.surname ? this.surname : "";
        var fullName = _.trim(name + " " + surname);
        return fullName;
    },
    getType: function () {
        return 'user';
    },
    getCollaborationGroups: function () {
        return _.map(this.collaborations, 'group');
    },
    isAdmin: function () {
        return this.role === this.ADMINISTRATOR;
    },
    admins: function (group) {
        var administeredGroupsId = _.map(this.admininstratedGroups, 'id');
        return _.includes(administeredGroupsId, group.id);
    },
    //sTODO: move to a service
    getReferenceBrowsingUrl: function () {
        return '/users/' + this.id + '/references';
    },
    getExternalConnectors: function () {
        var connectors = [];
        var publicationsConnector = {name: 'Publications', enabled: true};
        var orcidConnector = {name: 'ORCID', enabled: !!this.orcidId};
        var scopusConnector = {name: 'Scopus', enabled: !!this.scopusId};
        connectors.push(publicationsConnector);
        connectors.push(orcidConnector);
        connectors.push(scopusConnector);
        return connectors;
    },
    getNewDocument: function (documentTypeObj) {
        var documentData = {
            draftCreator: this.id,
            draft: true,
            type: documentTypeObj.key,
            sourceType: documentTypeObj.defaultSource
        };
        return Scientilla.reference.create(documentData);
    },
    getProfileUrl: function () {
        return '/users/' + this.id;
    }

};

//sTodo: evaluating wheter to reformat in a constructor style
Scientilla.reference = {
    UNKNOWN_REFERENCE: 0,
    USER_REFERENCE: 1,
    GROUP_REFERENCE: 2,
    DRAFT: 'draft',
    VERIFIED: 'verified',
    PUBLIC: 'public',
    create: function (referenceData) {
        var fields = [
            'authors',
            'title',
            'year',
            'journal',
            'issue',
            'volume',
            'pages',
            'articleNumber',
            'doi',
            'bookTitle',
            'editor',
            'publisher',
            'conferenceName',
            'conferenceLocation',
            'acronym',
            'type',
            'sourceType',
            'scopusId',
            'wosId'
        ];
        var reference = _.pick(referenceData, fields);
        _.extend(reference, Scientilla.reference);
        return reference;
    },
    getAllCoauthors: function () {
        return _.union(this.privateCoauthors, this.publicCoauthors);
    },
    getType: function () {
        if (!!this.owner)
            return this.USER_REFERENCE;
        else if (!!this.groupOwner)
            return this.GROUP_REFERENCE;
        else
            return this.UNKNOWN_REFERENCE;
    },
    getAuthors: function () {
        if (!this.authors)
            return [];

        return this.authors.replace(/\s+et all\s*$/i, '').split(',').map(_.trim);
    },
    getUcAuthors: function () {
        var authors = this.getAuthors();
        var ucAuthors = _.map(authors, function (a) {
            return a.toUpperCase();
        });
        return ucAuthors;
    },
    getDisplayName: function () {
        return this.getDisplayName();
    },
    getNewGroupReference: function (groupId) {
        return {
            title: "",
            authors: "",
            draftGroupCreator: groupId,
            draft: true
        };
    },
    getNewDraftReference: function (userId) {
        return {
            title: "",
            authors: "",
            draftCreator: userId,
            draft: true
        };
    },
    getDraftCreator: function () {
        if (this.draftCreator)
            return this.draftCreator;
        return this.draftGroupCreator;
    },
    getDocumentsFields: function (source) {
        var fields;
        if (source === 'journal')
            fields = {
                title: {
                    inputType: 'text'
                },
                authors: {
                    inputType: 'text'
                },
                year: {
                    inputType: 'text'
                },
                journal: {
                    inputType: 'text'
                },
                issue: {
                    inputType: 'text'
                },
                volume: {
                    inputType: 'text'
                },
                pages: {
                    inputType: 'text'
                },
                articleNumber: {
                    label: "Article number",
                    inputType: 'text'
                },
                doi: {
                    label: "DOI",
                    inputType: 'text'
                },
                scopusId: {
                    label: "Scopus ID",
                    inputType: 'text'
                },
                wosId: {
                    label: "WOS ID",
                    inputType: 'text'
                }
            };
        if (source === 'book')
            fields = {
                title: {
                    inputType: 'text'
                },
                bookTitle: {
                    label: 'Book title',
                    inputType: 'text'
                },
                authors: {
                    inputType: 'text'
                },
                year: {
                    inputType: 'text'
                },
                pages: {
                    inputType: 'text'
                },
                editor: {
                    inputType: 'text'
                },
                publisher: {
                    inputType: 'text'
                },
                scopusId: {
                    label: "Scopus ID",
                    inputType: 'text'
                },
                wosId: {
                    label: "WOS ID",
                    inputType: 'text'
                }
            };
        if (source === 'conference')
            fields = {
                title: {
                    inputType: 'text'
                },
                authors: {
                    inputType: 'text'
                },
                year: {
                    inputType: 'text'
                },
                conferenceName: {
                    label: 'Conference name',
                    inputType: 'text'
                },
                conferenceLocation: {
                    label: 'Conference location',
                    inputType: 'text'
                },
                acronym: {
                    inputType: 'text'
                },
                scopusId: {
                    label: "Scopus ID",
                    inputType: 'text'
                },
                wosId: {
                    label: "WOS ID",
                    inputType: 'text'
                }
            };
        if (!source) {
            fields = {
                title: {
                    inputType: 'text'
                },
                authors: {
                    inputType: 'text'
                },
                year: {
                    inputType: 'text'
                },
                scopusId: {
                    label: "Scopus ID",
                    inputType: 'text'
                },
                wosId: {
                    label: "WOS ID",
                    inputType: 'text'
                }
            };
        }
        return fields;
    },
    getDocumentTypes: function () {
        return [
            {
                key: 'article',
                label: 'Article',
                defaultSource: 'journal'
            },
            {
                key: 'article_in_press',
                label: 'Article in Press',
                defaultSource: 'journal'
            },
            {
                key: 'abstract_report',
                label: 'Abstract Report',
                defaultSource: null
            },
            {
                key: 'book',
                label: 'Book',
                defaultSource: 'book'
            },
            {
                key: 'book_chapter',
                label: 'Book Chapter',
                defaultSource: 'book'
            },
            {
                key: 'conference_paper',
                label: 'Conference Paper',
                defaultSource: 'conference'
            },
            {
                key: 'conference_review',
                label: 'Conference Review',
                defaultSource: 'conference'
            },
            {
                key: 'editorial',
                label: 'Editorial',
                defaultSource: null
            },
            {
                key: 'erratum',
                label: 'Erratum',
                defaultSource: null
            },
            {
                key: 'letter',
                label: 'Letter',
                defaultSource: null
            },
            {
                key: 'note',
                label: 'Note',
                defaultSource: null
            },
            {
                key: 'report',
                label: 'Report',
                defaultSource: null
            },
            {
                key: 'review',
                label: 'Review',
                defaultSource: 'journal'
            },
            {
                key: 'short_survey',
                label: 'Short Survey',
                defaultSource: null
            },
            {
                key: 'paper',
                label: 'Paper',
                defaultSource: 'conference'
            }
        ];
    },
    copyDocument: function (document, creator) {

        var excludedFields = ['draft', 'draftCreator', 'draftGroupCreator'];

        var documentTypeObj = {
            key: document.type,
            defaultSource: document.sourceType
        };

        var newDoc = creator.getNewDocument(documentTypeObj);

        _.forOwn(newDoc, function (value, key) {
            if (_.includes(excludedFields, key))
                return;

            newDoc[key] = document[key];
        });

        return newDoc;
    },
    isDiscarded: function() {
        return !!this.discarded;
    }
};

Scientilla.membership = {
    getDisplayName: function () {
        //sTODO: to be removed when deep populate is implemented
        if (_.isFunction(this.user.getDisplayName))
            return this.user.getDisplayName();
        else
            return '';
    }
};

Scientilla.collaboration = {
    getDisplayName: function () {
        //sTODO: to be removed when deep populate is implemented
        if (_.isFunction(this.group.getDisplayName))
            return this.group.getDisplayName();
        else
            return '';
    }
};

Scientilla.group = {
    getDisplayName: function () {
        return this.name;
    },
    getType: function () {
        return 'group';
    },
    //sTODO: move to a service
    getReferenceBrowsingUrl: function () {
        return '/groups/' + this.id + '/references';
    },
    getExternalConnectors: function () {
        var connectors = [];
        var publicationsConnector = {name: 'Publications', enabled: !!this.publicationsAcronym};
        var scopusConnector = {name: 'Scopus', enabled: !!this.scopusId};
        connectors.push(publicationsConnector);
        connectors.push(scopusConnector);
        return connectors;
    },
    getNewDocument: function (documentTypeObj) {
        var documentData = {
            draftCreator: this.id,
            draft: true,
            type: documentTypeObj.key,
            sourceType: documentTypeObj.defaultSource
        };
        return Scientilla.reference.create(documentData);
    },
    getProfileUrl: function () {
        return '/groups/' + this.id;
    }
};

Scientilla.toDocumentsCollection = function (documents) {
    _.each(documents, function (d) {
        _.assign(d, Scientilla.reference);
        Scientilla.toUsersCollection(d.privateCoauthors);
    });
};

Scientilla.toUsersCollection = function (users) {
    _.each(users, function (u) {
        _.assign(u, Scientilla.user);
    });
};
<|endoftext|>


<commit_before>// arguments - node element, event to trigger and function to execute
// addEvent function
const addEvent = (elem, type, func) => {
  if (elem.addEventListener) {
    elem.addEventListener(type, func, false);
  } else {
    elem.attachEvent(`on${type}`, func);
  }
};

// defining removeEvent function
const removeEvent = (elem, type, func) => {
  if (elem.removeEventListener) {
    elem.removeEventListener(type, func, false);
  } else {
    elem.detachEvent(`on${type}`, func);
  }
};

// exporting functions
module.exports = {
  addEvent,
  removeEvent
};
<commit_msg>replace arrow functions with es5 functions<commit_after>// arguments - node element, event to trigger and function to execute
// addEvent function
function addEvent(elem, type, func) {
  if (elem.addEventListener) {
    elem.addEventListener(type, func, false);
  } else {
    elem.attachEvent(`on${type}`, func);
  }
}

// defining removeEvent function
function removeEvent(elem, type, func) {
  if (elem.removeEventListener) {
    elem.removeEventListener(type, func, false);
  } else {
    elem.detachEvent(`on${type}`, func);
  }
}

// exporting functions
module.exports = {
  addEvent,
  removeEvent
};
<|endoftext|>


<commit_before>'use strict';

/**
 * @name open311
 * @description controller to handle open311 compliant requests
 * @see {@link http://wiki.open311.org/GeoReport_v2/| GeoReporter V2}
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */

//dependencies
const _ = require('lodash');
const config = require('config');
const mongoose = require('mongoose');
const Service = mongoose.model('Service');
const ServiceRequest = mongoose.model('ServiceRequest');

//constants
const OPEN311_STATUS_OPEN = 'open';
const parseDate = function (date) {
  date = Date.parse(date);
  date = (date ? new Date(date) : undefined);
  return date;
};

//TODO handle date & other open 311 filter on get request(s) status
//TODO handle specific client issues
//TODO present error in 311 specifications
//TODO add support to API Key(client id & client credentials)

module.exports = {
  /**
   * @name discovery
   * @description handle /discovery request
   * @param  {HttpRequest} request  http request
   * @param  {HttpResponse} response http response
   * @since 0.1.0
   * @version 0.1.0
   * @public
   */
  discovery: function (request, response /*, next*/ ) {
    const _config = config.get('open311');
    response.ok(_config.discovery);
  },

  /**
   * @name services
   * @description handle /services request.
   *
   *              It provides a list of all acceptable service request types
   *              and their associated service codes.
   *
   *
   * @param  {HttpRequest} request  http request
   * @param  {HttpResponse} response http response
   * @since 0.1.0
   * @version 0.1.0
   * @public
   */
  services: function (request, response, next) {

    //initialize query criteria
    let criteria = { isExternal: true };

    //obtain provided jurisdiction criteria
    const jurisdiction = _.get(request, 'query.jurisdiction_id');

    //merge & clean criteria
    criteria = _.merge({}, criteria, { jurisdiction: jurisdiction });
    criteria = _.omitBy(criteria, _.isUndefined);

    Service
      .find(criteria)
      .exec(function (error, services) {
        if (error) {
          next(error);
        } else {
          //map services to open311 compliant service list
          services = _.map(services, function (service) {
            return service.toOpen311();
          });

          response.ok(services);
        }
      });
  },

  /**
   * @function
   * @name open311.index()
   * @description display a list of all service request in open311 compliant fomart
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   * @see {@link http://wiki.open311.org/GeoReport_v2/#get-service-requests}
   */
  index: function (request, response, next) {
    //TODO filter per client
    //TODO filter by service_request_id
    //TODO filter by service_code

    //initialize query criteria
    let criteria = {};

    //obtain provided jurisdiction criteria
    criteria.jurisdiction = _.get(request, 'query.jurisdiction_id');

    //filter by provided status criteria
    criteria.status = _.get(request, 'query.status');
    if (!_.isEmpty(criteria.status)) {
      criteria.resolvedAt =
        (criteria.status === OPEN311_STATUS_OPEN ? { $eq: null } : { $ne: null });
    }
    delete criteria.status;

    //filter by provided start date criteria
    criteria.startedAt = parseDate(_.get(request, 'query.start_date'));
    if (_.isDate(criteria.startedAt)) {
      criteria.createdAt = { $gte: criteria.startedAt };
    }
    delete criteria.startedAt;

    //filter by provided end date criteria
    criteria.endedAt = parseDate(_.get(request, 'query.end_date'));
    if (_.isDate(criteria.endedAt)) {
      criteria.createdAt =
        _.merge({}, criteria.createdAt, { $lte: criteria.endedAt });
    }
    delete criteria.endedAt;

    //merge & clean criteria
    criteria = _.omitBy(criteria, _.isUndefined);

    ServiceRequest
      .find(criteria)
      .exec(function (error, serviceRequests) {
        if (error) {
          next(error);
        } else {
          //map serviceRequests to open311 compliant serviceRequest list
          serviceRequests =
            _.map(serviceRequests, function (serviceRequest) {
              return serviceRequest.toOpen311();
            });

          response.ok(serviceRequests);
        }
      });
  },

  /**
   * @function
   * @name open311.create()
   * @description create a new service request from open311 compliant client
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   */
  create: function (request, response, next) {
    //TODO support media upload & media_url
    let serviceRequest = request.body;

    ServiceRequest
      .createFromOpen311Client(serviceRequest,
        function (error, open311Response /*, serviceRequest*/ ) {
          console.log('submit error', error);
          if (error) {
            next(error);
          } else {
            response.created(open311Response);
          }
        });
  },

  /**
   * @function
   * @name open311.show()
   * @description display a specific service request in open 311 compliant format
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   */
  show: function (request, response, next) {
    //obtain service request code
    const code = request.params.id;

    ServiceRequest
      .findOne({ code: code })
      .exec(function (error, serviceRequest) {
        if (error || !serviceRequest) {
          if (!error) {
            error = new Error('Not Found');
            error.status = 404;
          }
          next(error);
        } else {
          response.ok([serviceRequest.toOpen311()]);
        }
      });
  }

};
<commit_msg>rectify open311 client submission bug<commit_after>'use strict';

/**
 * @name open311
 * @description controller to handle open311 compliant requests
 * @see {@link http://wiki.open311.org/GeoReport_v2/| GeoReporter V2}
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */

//dependencies
const _ = require('lodash');
const config = require('config');
const mongoose = require('mongoose');
const Service = mongoose.model('Service');
const ServiceRequest = mongoose.model('ServiceRequest');

//constants
const OPEN311_STATUS_OPEN = 'open';
const parseDate = function (date) {
  date = Date.parse(date);
  date = (date ? new Date(date) : undefined);
  return date;
};

//TODO handle date & other open 311 filter on get request(s) status
//TODO handle specific client issues
//TODO present error in 311 specifications
//TODO add support to API Key(client id & client credentials)

module.exports = {
  /**
   * @name discovery
   * @description handle /discovery request
   * @param  {HttpRequest} request  http request
   * @param  {HttpResponse} response http response
   * @since 0.1.0
   * @version 0.1.0
   * @public
   */
  discovery: function (request, response /*, next*/ ) {
    const _config = config.get('open311');
    response.ok(_config.discovery);
  },

  /**
   * @name services
   * @description handle /services request.
   *
   *              It provides a list of all acceptable service request types
   *              and their associated service codes.
   *
   *
   * @param  {HttpRequest} request  http request
   * @param  {HttpResponse} response http response
   * @since 0.1.0
   * @version 0.1.0
   * @public
   */
  services: function (request, response, next) {

    //initialize query criteria
    let criteria = { isExternal: true };

    //obtain provided jurisdiction criteria
    const jurisdiction = _.get(request, 'query.jurisdiction_id');

    //merge & clean criteria
    criteria = _.merge({}, criteria, { jurisdiction: jurisdiction });
    criteria = _.omitBy(criteria, _.isUndefined);

    Service
      .find(criteria)
      .exec(function (error, services) {
        if (error) {
          next(error);
        } else {
          //map services to open311 compliant service list
          services = _.map(services, function (service) {
            return service.toOpen311();
          });

          response.ok(services);
        }
      });
  },

  /**
   * @function
   * @name open311.index()
   * @description display a list of all service request in open311 compliant fomart
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   * @see {@link http://wiki.open311.org/GeoReport_v2/#get-service-requests}
   */
  index: function (request, response, next) {
    //TODO filter per client
    //TODO filter by service_request_id
    //TODO filter by service_code

    //initialize query criteria
    let criteria = {};

    //obtain provided jurisdiction criteria
    criteria.jurisdiction = _.get(request, 'query.jurisdiction_id');

    //filter by provided status criteria
    criteria.status = _.get(request, 'query.status');
    if (!_.isEmpty(criteria.status)) {
      criteria.resolvedAt =
        (criteria.status === OPEN311_STATUS_OPEN ? { $eq: null } : { $ne: null });
    }
    delete criteria.status;

    //filter by provided start date criteria
    criteria.startedAt = parseDate(_.get(request, 'query.start_date'));
    if (_.isDate(criteria.startedAt)) {
      criteria.createdAt = { $gte: criteria.startedAt };
    }
    delete criteria.startedAt;

    //filter by provided end date criteria
    criteria.endedAt = parseDate(_.get(request, 'query.end_date'));
    if (_.isDate(criteria.endedAt)) {
      criteria.createdAt =
        _.merge({}, criteria.createdAt, { $lte: criteria.endedAt });
    }
    delete criteria.endedAt;

    //merge & clean criteria
    criteria = _.omitBy(criteria, _.isUndefined);

    ServiceRequest
      .find(criteria)
      .exec(function (error, serviceRequests) {
        if (error) {
          next(error);
        } else {
          //map serviceRequests to open311 compliant serviceRequest list
          serviceRequests =
            _.map(serviceRequests, function (serviceRequest) {
              return serviceRequest.toOpen311();
            });

          response.ok(serviceRequests);
        }
      });
  },

  /**
   * @function
   * @name open311.create()
   * @description create a new service request from open311 compliant client
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   */
  create: function (request, response, next) {
    //TODO support media upload & media_url
    let serviceRequest = request.body;

    ServiceRequest
      .createFromOpen311Client(serviceRequest,
        function (error, open311Response /*, serviceRequest*/ ) {
          if (error) {
            next(error);
          } else {
            response.created(open311Response);
          }
        });
  },

  /**
   * @function
   * @name open311.show()
   * @description display a specific service request in open 311 compliant format
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   */
  show: function (request, response, next) {
    //obtain service request code
    const code = request.params.id;

    ServiceRequest
      .findOne({ code: code })
      .exec(function (error, serviceRequest) {
        if (error || !serviceRequest) {
          if (!error) {
            error = new Error('Not Found');
            error.status = 404;
          }
          next(error);
        } else {
          response.ok([serviceRequest.toOpen311()]);
        }
      });
  }

};
<|endoftext|>


<commit_before>'use strict';

var Polls = require('../models/polls.js');
var Users = require('../models/users.js');
var appUrl = process.env.APP_URL

function createPollId() {
    var charArr = [];
    var charChoices = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    var lenOfId = 10;
    for (var i=0; i<lenOfId; i++) {
      charArr.push(charChoices.charAt(Math.floor(Math.random() * charChoices.length)));
    }

    return charArr.join('');
}

function PollHandler() {

  this.getAllPolls = function (req, res) {
    Polls
      .find()
      .exec(function (err, result) {
        if (err) { throw err; }
        if (result) {
          // console.log("result......");
          // console.log(result);
          var resultObj = {};
          for (var i=0; i<result.length; i++) {
            var pollId = result[i]['pollId'];
            resultObj[pollId] = result[i];
          }

          res.json(resultObj);
        }
      });
  };

  this.getUserPollIds = function(req, res) {
    Users
      .findOne({ 'twitter.id': req.user.twitter.id }, { '_id': false })
      .exec(function (err, result) {
        if (err) { throw err; }

        res.json(result.userPolls);
      });
  };

  this.addPoll = function (req, res) {
    var optionsString = req.body.pollOptions;
    var optionsArr = optionsString.split("\r\n");

    // If there is a newline at the very end, delete this from the array.
    if (optionsArr[optionsArr.length - 1] === "") {
      optionsArr = optionsArr.slice(0, optionsArr.length-1);
    }

    var optionObjectArr = [];
    for (var i=0; i<optionsArr.length; i++) {
      var currOption = {
        optionKey: i,
                optionText: optionsArr[i],
                optionCount: 0
      }
      optionObjectArr.push(currOption);
    }
    var pollId = createPollId();

    Polls
      .create(
        {
          'userId': req.user.twitter.id,
          'pollId': pollId,
          'title': req.body.pollTitle,
          'options': optionObjectArr
        });

    Users
      .findOneAndUpdate({ 'twitter.id': req.user.twitter.id },
        {
          $push: {
              'userPolls.pollIdList': pollId
          }
        })
      .exec(function (err, result) {
          if (err) {
            res.send(null, 500);
          }
          // console.log(result.userPolls);
          // res.json(result.userPolls)
        }
      );

  res.redirect('/polldetails/' + pollId);

  };

  this.deletePoll = function(req, res) {
    // console.log("printing req.body in deletepoll....");
    // console.log(req.body);
    var pollId = req.body.pollId;
    // alert("Are you sure you want to delete this poll?");

    Users
      .findOne({ 'twitter.id': req.user.twitter.id }, function(err, doc) {
        if (err) {
          res.send(null, 500);
        }
        else if (doc) {
          var userPollsArr = doc.userPolls.pollIdList;
          userPollsArr.splice(userPollsArr.indexOf(pollId), 1);

          doc.save(function(err, doc) {
            if (err) {
              res.send(null, 500);
            }
          })
        }
      });

    Polls
      .findOneAndRemove({ 'pollId': pollId }, function(err, doc) {
        if (err) {
          res.send(null, 500);
        }
      });

  };

  this.votePoll = function(req, res) {
    var choice = req.body.choice;
    var pollId = req.query.pollid;
    console.log(choice);
    console.log("pollId: " + pollId);

    Polls
      .findOne({ 'pollId': pollId }, function(err, doc) {
        if (err) {
          res.send(null, 500);
        }
        else if (doc) {
          var options = doc.options;
          for (var i=0; i<options.length; i++) {
            // console.log("####");
            // console.log("optionText: " + options[i].optionText);
            // console.log("choice: " + choice);
            // console.log("####");
            if (options[i].optionText == choice) {
              // console.log("incre count");
              options[i].optionCount++;
            }
          }
          // console.log(options);

          doc.save(function(err, doc) {
            if (err) {
              res.send(null, 500);
            }
          })

        }
      });

  };

}

module.exports = PollHandler;<commit_msg>Fix options processing so it ignores blank line option entries<commit_after>'use strict';

var Polls = require('../models/polls.js');
var Users = require('../models/users.js');
var appUrl = process.env.APP_URL

function createPollId() {
    var charArr = [];
    var charChoices = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    var lenOfId = 10;
    for (var i=0; i<lenOfId; i++) {
      charArr.push(charChoices.charAt(Math.floor(Math.random() * charChoices.length)));
    }

    return charArr.join('');
}

function PollHandler() {

  this.getAllPolls = function (req, res) {
    Polls
      .find()
      .exec(function (err, result) {
        if (err) { throw err; }
        if (result) {
          // console.log("result......");
          // console.log(result);
          var resultObj = {};
          for (var i=0; i<result.length; i++) {
            var pollId = result[i]['pollId'];
            resultObj[pollId] = result[i];
          }

          res.json(resultObj);
        }
      });
  };

  this.getUserPollIds = function(req, res) {
    Users
      .findOne({ 'twitter.id': req.user.twitter.id }, { '_id': false })
      .exec(function (err, result) {
        if (err) { throw err; }

        res.json(result.userPolls);
      });
  };

  this.addPoll = function (req, res) {
    var optionsString = req.body.pollOptions;
    var optionsArr = optionsString.split("\r\n");

    // If there is a newline at the very end, delete this from the array.
    // if (optionsArr[optionsArr.length - 1] === "") {
    //   optionsArr = optionsArr.slice(0, optionsArr.length-1);
    // }

    var optionObjectArr = [];
    for (var i=0; i<optionsArr.length; i++) {
      if (optionsArr[i] === "") {
        continue;
      }

      var currOption = {
        optionKey: i,
                optionText: optionsArr[i],
                optionCount: 0
      }
      optionObjectArr.push(currOption);
    }
    var pollId = createPollId();

    Polls
      .create(
        {
          'userId': req.user.twitter.id,
          'pollId': pollId,
          'title': req.body.pollTitle,
          'options': optionObjectArr
        });

    Users
      .findOneAndUpdate({ 'twitter.id': req.user.twitter.id },
        {
          $push: {
              'userPolls.pollIdList': pollId
          }
        })
      .exec(function (err, result) {
          if (err) {
            res.send(null, 500);
          }
          // console.log(result.userPolls);
          // res.json(result.userPolls)
        }
      );

  res.redirect('/polldetails/' + pollId);

  };

  this.deletePoll = function(req, res) {
    // console.log("printing req.body in deletepoll....");
    // console.log(req.body);
    var pollId = req.body.pollId;
    // alert("Are you sure you want to delete this poll?");

    Users
      .findOne({ 'twitter.id': req.user.twitter.id }, function(err, doc) {
        if (err) {
          res.send(null, 500);
        }
        else if (doc) {
          var userPollsArr = doc.userPolls.pollIdList;
          userPollsArr.splice(userPollsArr.indexOf(pollId), 1);

          doc.save(function(err, doc) {
            if (err) {
              res.send(null, 500);
            }
          })
        }
      });

    Polls
      .findOneAndRemove({ 'pollId': pollId }, function(err, doc) {
        if (err) {
          res.send(null, 500);
        }
      });

  };

  this.votePoll = function(req, res) {
    var choice = req.body.choice;
    var pollId = req.query.pollid;
    console.log(choice);
    console.log("pollId: " + pollId);

    Polls
      .findOne({ 'pollId': pollId }, function(err, doc) {
        if (err) {
          res.send(null, 500);
        }
        else if (doc) {
          var options = doc.options;
          for (var i=0; i<options.length; i++) {
            // console.log("####");
            // console.log("optionText: " + options[i].optionText);
            // console.log("choice: " + choice);
            // console.log("####");
            if (options[i].optionText == choice) {
              // console.log("incre count");
              options[i].optionCount++;
            }
          }
          // console.log(options);

          doc.save(function(err, doc) {
            if (err) {
              res.send(null, 500);
            }
          })

        }
      });

  };

}

module.exports = PollHandler;<|endoftext|>


<commit_before>// Advanced Topics: Back End Web Development with Node.js

var request = require('request');
var async = require('async');
var calculateScore = require('../calculateScore.js');

module.exports = function(url, cb) {
	if (!url) {
		return cb("URL not found");
	}
	var tests = [{
		description: "submitted URL was accessible from the internet",
		assert: function(url, cb) {
			request(url, function(error, response) {
				if (!error && response.statusCode == 200) {
					this.passed = true;
					cb(null, this);
				} else {
					this.passed = false;
					cb(null, this);
				}
			}.bind(this));
		}
	}];

	async.map(tests, function(test, cb) {
		test.assert(url, cb);
	}, function() {
		for (var test of tests) {
			delete test.assert;
		}
		var scoreObject = {
			score: calculateScore(tests),
			tests: tests,
			url: url
		};
		return cb(null, scoreObject);
	});
};

// var tests = [{
// 	description: "server responds to the root URL",
// 	assert: function(url, cb) {
// 		request(url, function(error, response) {
// 			if (!error && response.statusCode == 200) {
// 				// console.log(response.body);
// 				this.passed = true;
// 				cb(null, this);
// 			} else {
// 				this.passed = false;
// 				cb(null, this);
// 			}
// 		}.bind(this));
// 	}
// }];
// for (var name of names) {
// 	tests.push({
// 		name: name,
// 		description: "a request to `/hello/" + name + "` returns " + '`Hello, ' + name + "!`",
// 		assert: function(url, cb) {
// 			request(url + "/hello/" + this.name, function(error, response) {
// 				if (!error && response.statusCode == 200) {
// 					if (response.body == "Hello, " + this.name + "!") {
// 						this.passed = true;
// 						cb(null, this);
// 					} else {
// 						this.passed = false;
// 						cb(null, this);
// 					}
// 				} else {
// 					this.passed = false;
// 					cb(null, this);
// 				}
// 			}.bind(this));
// 		}
// 	});
// }
<commit_msg>tweaking assignment 18<commit_after>// Advanced Topics: Back End Web Development with Node.js

var request = require('request');
var async = require('async');
var calculateScore = require('../calculateScore.js');

var names = ["kirk", "spock", "bones", "uruha", "scotty", "chekov", "sulu"];

module.exports = function(url, cb) {
	if (!url) {
		return cb("URL not found");
	}

	var tests = [{
		description: "server responds to the root URL",
		assert: function(url, cb) {
			request(url, function(error, response) {
				if (!error && response.statusCode == 200) {
					// console.log(response.body);
					this.passed = true;
					cb(null, this);
				} else {
					this.passed = false;
					cb(null, this);
				}
			}.bind(this));
		}
	}];

	for (var name of names) {
		tests.push({
			name: name,
			description: "a request to `/hello/" + name + "` returns " + '`Hello, ' + name + "!`",
			assert: function(url, cb) {
				let testingURL;
				if (url.endsWith("/")) {
					testingURL = url + "hello/" + this.name;
				} else {
					testingURL = url + "/hello/" + this.name;
				}
				request(testingURL, function(error, response) {
					if (!error && response.statusCode == 200) {
						if (response.body == "Hello, " + this.name + "!") {
							this.passed = true;
							cb(null, this);
						} else {
							this.passed = false;
							cb(null, this);
						}
					} else {
						this.passed = false;
						cb(null, this);
					}
				}.bind(this));
			}
		});
	}

	async.map(tests, function(test, cb) {
		test.assert(url, cb);
	}, function() {
		for (var test of tests) {
			delete test.assert;
		}
		var scoreObject = {
			score: calculateScore(tests),
			tests: tests,
			url: url
		};
		return cb(null, scoreObject);
	});
};
<|endoftext|>


<commit_before>// @flow
import rootReducer from '../reducers'
import storeEnhancer from './enhancer.platform'
import thunkMiddleware from 'redux-thunk'
import {actionLogger} from './action-logger'
import {closureCheck} from './closure-check'
import {convertToError} from '../util/errors'
import {createLogger} from 'redux-logger'
import {createStore} from 'redux'
import {
  enableStoreLogging,
  enableActionLogging,
  closureStoreCheck,
  immediateStateLogging,
} from '../local-debug'
import {globalError} from '../constants/config'
import {isMobile} from '../constants/platform'
import {run as runSagas, create as createSagaMiddleware} from './configure-sagas'
import {setupLogger, immutableToJS} from '../util/periodic-logger'

let theStore: Store

const crashHandler = error => {
  if (__DEV__) {
    throw error
  }
  if (theStore) {
    theStore.dispatch({
      payload: convertToError(error),
      type: globalError,
    })
  } else {
    console.warn('Got crash before store created?', error)
  }
}

let loggerMiddleware: any

if (enableStoreLogging) {
  const logger = setupLogger('storeLogger', 100, immediateStateLogging, immutableToJS, 50, true)
  loggerMiddleware = createLogger({
    actionTransformer: (...args) => {
      console.log('Action:', ...args)
      logger.log('Action:', ...args)
      return null
    },
    collapsed: true,
    duration: true,
    logger: {
      error: () => {},
      group: () => {},
      groupCollapsed: () => {},
      groupEnd: () => {},
      log: () => {},
      warn: () => {},
    },
    stateTransformer: (...args) => {
      logger.log('State:', ...args)
      return null
    },
    titleFormatter: () => null,
  })
}

let lastError = new Error('')

const errorCatching = store => next => action => {
  try {
    return next(action)
  } catch (error) {
    // Don't let the same error keep getting caught
    if (lastError.message === error.message) {
      return
    }
    lastError = error
    console.warn(`Caught a middleware exception ${error}`)

    try {
      crashHandler(error) // don't let this thing crash us forever
    } catch (_) {}
  }
}

let middlewares = [errorCatching, createSagaMiddleware(crashHandler), thunkMiddleware]

if (enableStoreLogging) {
  middlewares.push(loggerMiddleware)
} else if (enableActionLogging) {
  middlewares.push(actionLogger)
}

if (closureStoreCheck) {
  middlewares.push(closureCheck)
}

export default function configureStore(initialState: any) {
  const store = createStore(rootReducer, initialState, storeEnhancer(middlewares))
  theStore = store

  if (module.hot && !isMobile) {
    module.hot.accept('../reducers', () => {
      store.replaceReducer(require('../reducers').default)
    })
  }

  runSagas()
  return store
}
<commit_msg>add stack to crash logger<commit_after>// @flow
import rootReducer from '../reducers'
import storeEnhancer from './enhancer.platform'
import thunkMiddleware from 'redux-thunk'
import {actionLogger} from './action-logger'
import {closureCheck} from './closure-check'
import {convertToError} from '../util/errors'
import {createLogger} from 'redux-logger'
import {createStore} from 'redux'
import {
  enableStoreLogging,
  enableActionLogging,
  closureStoreCheck,
  immediateStateLogging,
} from '../local-debug'
import {globalError} from '../constants/config'
import {isMobile} from '../constants/platform'
import {run as runSagas, create as createSagaMiddleware} from './configure-sagas'
import {setupLogger, immutableToJS} from '../util/periodic-logger'

let theStore: Store

const crashHandler = error => {
  if (__DEV__) {
    throw error
  }
  if (theStore) {
    theStore.dispatch({
      payload: convertToError(error),
      type: globalError,
    })
  } else {
    console.warn('Got crash before store created?', error)
  }
}

let loggerMiddleware: any

if (enableStoreLogging) {
  const logger = setupLogger('storeLogger', 100, immediateStateLogging, immutableToJS, 50, true)
  loggerMiddleware = createLogger({
    actionTransformer: (...args) => {
      console.log('Action:', ...args)
      logger.log('Action:', ...args)
      return null
    },
    collapsed: true,
    duration: true,
    logger: {
      error: () => {},
      group: () => {},
      groupCollapsed: () => {},
      groupEnd: () => {},
      log: () => {},
      warn: () => {},
    },
    stateTransformer: (...args) => {
      logger.log('State:', ...args)
      return null
    },
    titleFormatter: () => null,
  })
}

let lastError = new Error('')

const errorCatching = store => next => action => {
  try {
    return next(action)
  } catch (error) {
    // Don't let the same error keep getting caught
    if (lastError.message === error.message) {
      return
    }
    lastError = error
    console.warn(`Caught a middleware exception ${error} ${error.stack}`)

    try {
      crashHandler(error) // don't let this thing crash us forever
    } catch (_) {}
  }
}

let middlewares = [errorCatching, createSagaMiddleware(crashHandler), thunkMiddleware]

if (enableStoreLogging) {
  middlewares.push(loggerMiddleware)
} else if (enableActionLogging) {
  middlewares.push(actionLogger)
}

if (closureStoreCheck) {
  middlewares.push(closureCheck)
}

export default function configureStore(initialState: any) {
  const store = createStore(rootReducer, initialState, storeEnhancer(middlewares))
  theStore = store

  if (module.hot && !isMobile) {
    module.hot.accept('../reducers', () => {
      store.replaceReducer(require('../reducers').default)
    })
  }

  runSagas()
  return store
}
<|endoftext|>


<commit_before>// DoB means Days of Buffering

function ynabEnhancedDoB() {
    var YNABheader = document.getElementsByClassName("budget-header-flexbox")[0];
    var elementForAoM = document.getElementsByClassName("budget-header-days")[0];
    var elementForDoB = elementForAoM.cloneNode(true);

    elementForDoB.className = elementForDoB.className + " days-of-buffering";
    elementForDoB.children[1].textContent = (ynabToolKit.l10nData && ynabToolKit.l10nData["toolkit.DoB"]) || "Days of Buffering";
    elementForDoB.children[1].title = "Don't like AoM? Try this out instead!";

    var calculation = ynabEnhancedDoBCalculate();
    if (!calculation){
        elementForDoB.children[0].textContent = "???";
        elementForDoB.children[0].title = "Your budget history is less than 15 days. Go on with YNAB a while.";
    }
    else {
        var dayText = (ynabToolKit.l10nData && ynabToolKit.l10nData["budget.ageOfMoneyDays.one"]) || "day";
        if (calculation.DoB > 1) dayText = (ynabToolKit.l10nData && ynabToolKit.l10nData["budget.ageOfMoneyDays.other"]) || "days";
        // Russian declension dummy.
        // if (ynabToolKit.options.l10n == 1){
        //   dayText = ynabToolKit.shared.declension('ru', calculation.DoB, {nom: 'день', gen: 'дня', plu: 'дней'});
        // }
        elementForDoB.children[0].textContent = calculation.DoB + " " + dayText;
        elementForDoB.children[0].title = "Total outflow: " + ynab.YNABSharedLib.currencyFormatter.format(calculation.totalOutflow) +
            "\nTotal days of budgeting: " + calculation.totalDays +
            "\nAverage daily outflow: ~" + ynab.YNABSharedLib.currencyFormatter.format(calculation.averageDailyOutflow) +
            "\nAverage daily transactions: " + calculation.averageDailyTransactions.toFixed(1);
    }

    YNABheader.appendChild(elementForDoB);
}

function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
}

function ynabEnhancedCheckTransactionTypes(transactions) {
    // Describe all handled transaction types and check that no other got.
    var handeledTransactionTypes = ["subTransaction", "transaction", "scheduledTransaction", "scheduledSubTransaction"];
    var uniqueTransactionTypes = Array.from(transactions, function(el) { return el.displayItemType; }).filter(onlyUnique);
    var allTypesHandeled = uniqueTransactionTypes.every(function(el) { return uniqueTransactionTypes.includes(el); });
    if (!allTypesHandeled) {
        throw "Found unhandeled transaction type. " + uniqueTransactionTypes;
    }
}

function ynabEnhancedDoBCalculate() {
    // Get outflow transactions.
    var entityManager = ynab.YNABSharedLib.defaultInstance.entityManager;
    var transactions = entityManager.getAllTransactions();
    var outflowTransactions = transactions.filter(function(el) {
      return !el.isTombstone &&
      el.transferAccountId === null &&
      el.amount < 0 &&
      el.getAccount().onBudget; });

    // Filter outflow transactions by Date for history lookup option.
    if (ynabToolKit.options.daysOfBufferingHistoryLookup > 0) {
      dateNow = Date.now();
      outflowTransactions = outflowTransactions.filter(function (el) {
        return (dateNow - el.getDate().getUTCTime()) / 3600/24/1000/(365/12) < ynabToolKit.options.daysOfBufferingHistoryLookup; });
    }

    // Get outflow transactions period
    outflowTransactionsDates = Array.from(outflowTransactions, function (el) { return el.getDate().getUTCTime(); });
    var firstTransactionDate = Math.min.apply(null, outflowTransactionsDates);
    var lastTransactionDate = Math.max.apply(null, outflowTransactionsDates);
    var totalDays = (lastTransactionDate - firstTransactionDate)/3600/24/1000;
    if (totalDays < 15){
        return false;
    }

    var totalOutflow = Array.from(outflowTransactions, function (i) { return -i.amount; }).reduce(function (a, b) { return a + b; }, 0);
    var averageDailyOutflow = totalOutflow / totalDays;
    var budgetAccountsTotal = ynab.YNABSharedLib.getBudgetViewModel_SidebarViewModel()._result.getOnBudgetAccountsBalance();
    return {
        DoB: Math.floor(budgetAccountsTotal/averageDailyOutflow),
        totalOutflow: totalOutflow,
        totalDays: totalDays,
        averageDailyOutflow: averageDailyOutflow,
        averageDailyTransactions: outflowTransactions.length/totalDays,
    };
}

function ynabEnhancedDoBInit() {
    var elementForAoM = document.getElementsByClassName("budget-header-days");
    var elementForDoB = document.getElementsByClassName('days-of-buffering');
    if (elementForAoM.length == 1 && elementForDoB.length === 0) {
        ynabEnhancedDoB();
    }

    setTimeout(ynabEnhancedDoBInit, 250);
}

setTimeout(ynabEnhancedDoBInit, 250);
<commit_msg>Refactor DoB<commit_after>// DoB means Days of Buffering
(function poll() {
  // Waits until an external function gives us the all clear that we can run (at /shared/main.js)
  if ( typeof ynabToolKit !== "undefined"  && ynabToolKit.pageReady === true ) {

    ynabToolKit.daysOfBuffering = (function(){

      // Supporting functions,
      // or variables, etc
      function onlyUnique(value, index, self) {
          return self.indexOf(value) === index;
      }

      function checkTransactionTypes(transactions) {
          // Describe all handled transaction types and check that no other got.
          var handeledTransactionTypes = ["subTransaction", "transaction", "scheduledTransaction", "scheduledSubTransaction"];
          var uniqueTransactionTypes = Array.from(transactions, function(el) { return el.displayItemType; }).filter(onlyUnique);
          var allTypesHandeled = uniqueTransactionTypes.every(function(el) { return uniqueTransactionTypes.includes(el); });
          if (!allTypesHandeled) {
              throw "Found unhandeled transaction type. " + uniqueTransactionTypes;
          }
      }

      function DoBCalculate() {
          // Get outflow transactions.
          var entityManager = ynab.YNABSharedLib.defaultInstance.entityManager;
          var transactions = entityManager.getAllTransactions();
          var outflowTransactions = transactions.filter(function(el) {
            return !el.isTombstone &&
            el.transferAccountId === null &&
            el.amount < 0 &&
            el.getAccount().onBudget; });

          // Filter outflow transactions by Date for history lookup option.
          if (ynabToolKit.options.daysOfBufferingHistoryLookup > 0) {
            dateNow = Date.now();
            outflowTransactions = outflowTransactions.filter(function (el) {
              return (dateNow - el.getDate().getUTCTime()) / 3600/24/1000/(365/12) < ynabToolKit.options.daysOfBufferingHistoryLookup; });
          }

          // Get outflow transactions period
          outflowTransactionsDates = Array.from(outflowTransactions, function (el) { return el.getDate().getUTCTime(); });
          var firstTransactionDate = Math.min.apply(null, outflowTransactionsDates);
          var lastTransactionDate = Math.max.apply(null, outflowTransactionsDates);
          var totalDays = (lastTransactionDate - firstTransactionDate)/3600/24/1000;
          if (totalDays < 15){
              return false;
          }

          var totalOutflow = Array.from(outflowTransactions, function (i) { return -i.amount; }).reduce(function (a, b) { return a + b; }, 0);
          var averageDailyOutflow = totalOutflow / totalDays;
          var budgetAccountsTotal = ynab.YNABSharedLib.getBudgetViewModel_SidebarViewModel()._result.getOnBudgetAccountsBalance();
          return {
              DoB: Math.floor(budgetAccountsTotal/averageDailyOutflow),
              totalOutflow: totalOutflow,
              totalDays: totalDays,
              averageDailyOutflow: averageDailyOutflow,
              averageDailyTransactions: outflowTransactions.length/totalDays,
          };
      }

      return {
        invoke: function() {
          var elementForAoM = document.getElementsByClassName("budget-header-days");
          var elementForDoB = document.getElementsByClassName('days-of-buffering');
          if (elementForAoM.length == 1 && elementForDoB.length === 0) {
              var YNABheader = document.getElementsByClassName("budget-header-flexbox")[0];
              elementForAoM = document.getElementsByClassName("budget-header-days")[0];
              elementForDoB = elementForAoM.cloneNode(true);

              elementForDoB.className = elementForDoB.className + " days-of-buffering";
              elementForDoB.children[1].textContent = (ynabToolKit.l10nData && ynabToolKit.l10nData["toolkit.DoB"]) || "Days of Buffering";
              elementForDoB.children[1].title = "Don't like AoM? Try this out instead!";

              var calculation = DoBCalculate();
              if (!calculation){
                  elementForDoB.children[0].textContent = "???";
                  elementForDoB.children[0].title = "Your budget history is less than 15 days. Go on with YNAB a while.";
              }
              else {
                  var dayText = (ynabToolKit.l10nData && ynabToolKit.l10nData["budget.ageOfMoneyDays.one"]) || "day";
                  if (calculation.DoB > 1) dayText = (ynabToolKit.l10nData && ynabToolKit.l10nData["budget.ageOfMoneyDays.other"]) || "days";
                  // Russian declension dummy.
                  // if (ynabToolKit.options.l10n == 1){
                  //   dayText = ynabToolKit.shared.declension('ru', calculation.DoB, {nom: 'день', gen: 'дня', plu: 'дней'});
                  // }
                  elementForDoB.children[0].textContent = calculation.DoB + " " + dayText;
                  elementForDoB.children[0].title = "Total outflow: " + ynab.YNABSharedLib.currencyFormatter.format(calculation.totalOutflow) +
                      "\nTotal days of budgeting: " + calculation.totalDays +
                      "\nAverage daily outflow: ~" + ynab.YNABSharedLib.currencyFormatter.format(calculation.averageDailyOutflow) +
                      "\nAverage daily transactions: " + calculation.averageDailyTransactions.toFixed(1);
              }

              YNABheader.appendChild(elementForDoB);
          }
        },

        observe: function(changedNodes) {
          // User has returned back to the budget screen
          // User switch budget month
          if (changedNodes.has('budget-header-flexbox') || changedNodes.has('budget-table')) {
            ynabToolKit.daysOfBuffering.invoke();
          }
        }
      };
    })(); // Keep feature functions contained within this object

    ynabToolKit.daysOfBuffering.invoke(); // Run your script once on page load

  } else {
    setTimeout(poll, 250);
  }
})();
<|endoftext|>


<commit_before>// @flow
import React from 'react'
import { Link } from 'react-router'
import { loadPostStream } from '../../actions/editorials'
import InvitationFormContainer from '../../containers/InvitationFormContainer'
import StreamContainer from '../../containers/StreamContainer'
import BackgroundImage from '../assets/BackgroundImage'
import RegistrationRequestForm from '../forms/RegistrationRequestForm'
import { EditorialOverlay, EditorialTitle, EditorialSubtitle, EditorialTools } from './EditorialParts'
import { css, media, select } from '../../styles/jss'
import * as s from '../../styles/jso'
import type { EditorialProps } from '../../types/flowtypes'

const baseStyle = css(
  s.relative,
  s.flex,
  s.flexWrap,
  s.fullWidth,
  s.fullHeight,
  s.px20,
  s.py20,
  s.bgcTransparent,
  media(s.minBreak2, s.px40, s.py40),
)

const headerStyle = css(
  s.relative,
  s.zIndex2,
  s.fullWidth,
  s.transitionOpacity,
)

const bodyStyle = css(
  s.relative,
  s.fullWidth,
  s.selfEnd,
)

const subtitleStyle = css(
  { ...bodyStyle },
  s.zIndex2,
)

const toolsStyle = css(
  { ...bodyStyle },
  s.zIndex4,
)

const linkStyle = css(
  s.absolute,
  s.flood,
  s.zIndex3,
  { color: 'rgba(0, 0, 0, 0)' },
  s.bgcCurrentColor,
)

const linkTextStyle = css(s.hidden)

// -------------------------------------

export const PostEditorial = (props: EditorialProps) => (

    { props.postPath &&
      <Link
        className={linkStyle}
        onClick={props.onClickEditorial}
        to={props.postPath}
      >
        {props.postPath}
      </Link>
    }

      <EditorialTitle label={props.editorial.get('title')} />

      <EditorialSubtitle label={props.editorial.get('subtitle')} />

      <EditorialTools isPostLoved={props.isPostLoved} postPath={props.postPath} />

    <EditorialOverlay />
    <BackgroundImage
      dpi={props.dpi}
      sources={props.sources}
      useGif
    />

)

// -------------------------------------

const postStreamBaseStyle = css(
  { ...baseStyle },
  { padding: '0 !important' },
  select('& .ElloMark.isSpinner > .SmileShape', { fill: '#fff !important' }),
)

export const PostStream = (props: EditorialProps) => (

    { props.postStreamHref &&
      <StreamContainer
        className="inEditorial"
        action={loadPostStream({
          endpointPath: props.postStreamHref,
          editorialTrackOptions: props.trackOptions,
          fallbackSources: props.sources,
          onClickEditorial: props.onClickEditorial,
          resultKey: `${props.editorialId}_${props.size}_${props.position}`,
          title: props.editorial.get('title'),
        })}
        shouldInfiniteScroll={false}
      />
    }

)

type PostProps = {
  dpi: string,
  detailPath: string,
  fallbackSources: string,
  isPostLoved: boolean,
  onClickEditorial: () => {},
  sources: any,
  title: string,
  username: string,
}

const postHeaderStyle = css(
  { ...headerStyle },
  media(s.minBreak2, { width: 'calc(100% - 35px)' }),
)

export const CuratedPost = (props: PostProps) => (

    { props.detailPath &&
      <Link
        className={linkStyle}
        onClick={props.onClickEditorial}
        to={props.detailPath}
      >
        {props.detailPath}
      </Link>
    }

      <EditorialTitle label={`${props.title} `} />
      <EditorialTitle label={`@${props.username}`} />

      <EditorialTools isPostLoved={props.isPostLoved} postPath={props.detailPath} />

    <EditorialOverlay />
    <BackgroundImage
      dpi={props.dpi}
      sources={props.sources || props.fallbackSources}
      useGif
    />

)

// -------------------------------------

export const LinkEditorial = (props: EditorialProps) => (

    {props.path && props.kind === 'internal' &&
      <Link
        className={linkStyle}
        onClick={props.onClickEditorial}
        to={props.path}
      >
        {props.url}
      </Link>
    }
    {props.url && props.kind === 'external' &&

        {props.url}

    }

      <EditorialTitle label={props.editorial.get('title')} />

      <EditorialSubtitle label={props.editorial.get('subtitle')} />

    <EditorialOverlay />
    <BackgroundImage
      dpi={props.dpi}
      sources={props.sources}
      useGif
    />

)

// -------------------------------------

const errorStyle = css(
  { ...baseStyle },
  s.flex,
  s.justifyCenter,
  s.itemsCenter,
  s.fontSize14,
  s.bgcRed,
  s.pointerNone,
)

const errorTextStyle = css(
  s.relative,
  s.zIndex2,
  s.colorWhite,
)

export const ErrorEditorial = () => (

    Something went wrong.
    <EditorialOverlay />

)

// -------------------------------------

const joinStyle = css(
  { ...baseStyle },
  s.colorWhite,
  select(
    '& .AuthenticationForm',
    s.absolute,
    { bottom: 0, maxWidth: '100%', width: '100%' },
  ),
  select(
    '& .JoinForm',
    s.relative,
  ),
  select('& h1', s.fontSize24, s.mt30),
  select('& h2', s.fontSize16),
)

export const JoinEditorial = (props: EditorialProps) => (

    <BackgroundImage
      className="hasOverlay6"
      dpi={props.dpi}
      sources={props.sources}
      useGif
    />
    <RegistrationRequestForm inEditorial />

)

// -------------------------------------

const inviteStyle = css(
  { ...baseStyle },
  s.colorWhite,
  select('& .InvitationsForm', s.mx0, s.relative, s.fullWidth, s.zIndex3),
  select('& .InvitationsForm form', s.absolute, s.fullWidth, { bottom: 0 }),
  select('& .BatchEmailControl .FormControlInput.isBoxControl',
    { height: '85px' },
  ),
  select('& h1', s.fontSize24),
  select('& header p', s.fontSize16),
  select('& .BatchEmailControlSuggestions', s.colorWhite, s.mb30),
)

export const InviteEditorial = (props: EditorialProps) => (

    <BackgroundImage
      className="hasOverlay6"
      dpi={props.dpi}
      sources={props.sources}
      useGif
    />

Invite some cool people

Help Ello grow.

    <InvitationFormContainer inEditorial />

)

<commit_msg>Fix up some styling around the invite module<commit_after>// @flow
import React from 'react'
import { Link } from 'react-router'
import { loadPostStream } from '../../actions/editorials'
import InvitationFormContainer from '../../containers/InvitationFormContainer'
import StreamContainer from '../../containers/StreamContainer'
import BackgroundImage from '../assets/BackgroundImage'
import RegistrationRequestForm from '../forms/RegistrationRequestForm'
import { EditorialOverlay, EditorialTitle, EditorialSubtitle, EditorialTools } from './EditorialParts'
import { css, media, select } from '../../styles/jss'
import * as s from '../../styles/jso'
import type { EditorialProps } from '../../types/flowtypes'

const baseStyle = css(
  s.relative,
  s.flex,
  s.flexWrap,
  s.fullWidth,
  s.fullHeight,
  s.px20,
  s.py20,
  s.bgcTransparent,
  media(s.minBreak2, s.px40, s.py40),
)

const headerStyle = css(
  s.relative,
  s.zIndex2,
  s.fullWidth,
  s.transitionOpacity,
)

const bodyStyle = css(
  s.relative,
  s.fullWidth,
  s.selfEnd,
)

const subtitleStyle = css(
  { ...bodyStyle },
  s.zIndex2,
)

const toolsStyle = css(
  { ...bodyStyle },
  s.zIndex4,
)

const linkStyle = css(
  s.absolute,
  s.flood,
  s.zIndex3,
  { color: 'rgba(0, 0, 0, 0)' },
  s.bgcCurrentColor,
)

const linkTextStyle = css(s.hidden)

// -------------------------------------

export const PostEditorial = (props: EditorialProps) => (

    { props.postPath &&
      <Link
        className={linkStyle}
        onClick={props.onClickEditorial}
        to={props.postPath}
      >
        {props.postPath}
      </Link>
    }

      <EditorialTitle label={props.editorial.get('title')} />

      <EditorialSubtitle label={props.editorial.get('subtitle')} />

      <EditorialTools isPostLoved={props.isPostLoved} postPath={props.postPath} />

    <EditorialOverlay />
    <BackgroundImage
      dpi={props.dpi}
      sources={props.sources}
      useGif
    />

)

// -------------------------------------

const postStreamBaseStyle = css(
  { ...baseStyle },
  { padding: '0 !important' },
  select('& .ElloMark.isSpinner > .SmileShape', { fill: '#fff !important' }),
)

export const PostStream = (props: EditorialProps) => (

    { props.postStreamHref &&
      <StreamContainer
        className="inEditorial"
        action={loadPostStream({
          endpointPath: props.postStreamHref,
          editorialTrackOptions: props.trackOptions,
          fallbackSources: props.sources,
          onClickEditorial: props.onClickEditorial,
          resultKey: `${props.editorialId}_${props.size}_${props.position}`,
          title: props.editorial.get('title'),
        })}
        shouldInfiniteScroll={false}
      />
    }

)

type PostProps = {
  dpi: string,
  detailPath: string,
  fallbackSources: string,
  isPostLoved: boolean,
  onClickEditorial: () => {},
  sources: any,
  title: string,
  username: string,
}

const postHeaderStyle = css(
  { ...headerStyle },
  media(s.minBreak2, { width: 'calc(100% - 35px)' }),
)

export const CuratedPost = (props: PostProps) => (

    { props.detailPath &&
      <Link
        className={linkStyle}
        onClick={props.onClickEditorial}
        to={props.detailPath}
      >
        {props.detailPath}
      </Link>
    }

      <EditorialTitle label={`${props.title} `} />
      <EditorialTitle label={`@${props.username}`} />

      <EditorialTools isPostLoved={props.isPostLoved} postPath={props.detailPath} />

    <EditorialOverlay />
    <BackgroundImage
      dpi={props.dpi}
      sources={props.sources || props.fallbackSources}
      useGif
    />

)

// -------------------------------------

export const LinkEditorial = (props: EditorialProps) => (

    {props.path && props.kind === 'internal' &&
      <Link
        className={linkStyle}
        onClick={props.onClickEditorial}
        to={props.path}
      >
        {props.url}
      </Link>
    }
    {props.url && props.kind === 'external' &&

        {props.url}

    }

      <EditorialTitle label={props.editorial.get('title')} />

      <EditorialSubtitle label={props.editorial.get('subtitle')} />

    <EditorialOverlay />
    <BackgroundImage
      dpi={props.dpi}
      sources={props.sources}
      useGif
    />

)

// -------------------------------------

const errorStyle = css(
  { ...baseStyle },
  s.flex,
  s.justifyCenter,
  s.itemsCenter,
  s.fontSize14,
  s.bgcRed,
  s.pointerNone,
)

const errorTextStyle = css(
  s.relative,
  s.zIndex2,
  s.colorWhite,
)

export const ErrorEditorial = () => (

    Something went wrong.
    <EditorialOverlay />

)

// -------------------------------------

const joinStyle = css(
  { ...baseStyle },
  s.colorWhite,
  select(
    '& .AuthenticationForm',
    s.absolute,
    { bottom: 0, maxWidth: '100%', width: '100%' },
  ),
  select(
    '& .JoinForm',
    s.relative,
  ),
  select('& h1', s.fontSize24, s.mt30),
  select('& h2', s.fontSize16),
)

export const JoinEditorial = (props: EditorialProps) => (

    <BackgroundImage
      className="hasOverlay6"
      dpi={props.dpi}
      sources={props.sources}
      useGif
    />
    <RegistrationRequestForm inEditorial />

)

// -------------------------------------

const inviteStyle = css(
  { ...baseStyle },
  s.colorWhite,
  select('& .InvitationsForm', s.mx0, s.relative, s.fullWidth, s.zIndex3),
  select('& .InvitationsForm form', s.absolute, s.fullWidth, { bottom: 0 }),
  select('& .InvitationsForm .FormButton', s.mt0),
  select('& .BatchEmailControl .FormControlInput.isBoxControl',
    { height: '85px' },
  ),
  select('& h1', s.fontSize24),
  select('& header p', s.fontSize16),
  select('& .BatchEmailControlSuggestions', s.colorWhite, s.mb30),
)

export const InviteEditorial = (props: EditorialProps) => (

    <BackgroundImage
      className="hasOverlay6"
      dpi={props.dpi}
      sources={props.sources}
      useGif
    />

Invite some cool people

Help Ello grow.

    <InvitationFormContainer inEditorial />

)

<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    // Screenshot base asset directory
    var sb = "/assets/app-screens/";

    // The apps page only lists actively supported, community made apps.
    // Since this list is mostly static, it will be hardcoded as a model.
    return [
      {
        "name": "Tenpenchii",
        "author": [
          { "name": "Tempest", "link": "http://hummingbird.me/users/Tempest" },
        ],
        "form": "Mobile",
        "platform": "Android",
        "screenshots": [sb+"tenpenchii/tenpenchii-1.jpg", sb+"tenpenchii/tenpenchii-2.jpg"],
        "desc": "Tenpenchii is a Hummingbird client for Android devices with a modern user interface and amazing functionality.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/android-tenpenchii/582" },
          { "name": "Download", "link": "https://play.google.com/store/apps/details?id=com.fractalemagic" },
        ]
      },
      {
        "name": "HummingList",
        "author": [
          { "name": "Riddle", "link": "http://hummingbird.me/users/riddle" },
        ],
        "form": "Mobile",
        "platform": "iOS",
        "screenshots": [sb+"humminglist/humminglist-1.jpg", sb+"humminglist/humminglist-2.jpg"],
        "desc": "Manage your Hummingbird library on the go from your iPhone, iPad or iPod touch.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/humminglist-ios-app/17003" },
          { "name": "Download", "link": "https://itunes.apple.com/us/app/humminglist/id866713967?mt=8" },
        ]
      },
      {
        "name": "Taiga",
        "author": [
          { "name": "Erengy", "link": "http://hummingbird.me/users/Erengy" },
        ],
        "form": "Desktop",
        "platform": "Windows",
        "screenshots": [sb+"taiga/taiga-1.jpg"],
        "desc": "Taiga helps you manage your list, discover new series, share watched episodes and download new ones.",
        "links": [
          { "name": "Discuss", "link": "http://forums.hummingbird.me/t/windows-taiga/10565" },
          { "name": "Download", "link": "http://taiga.erengy.com/download.php" },
        ]
      },
      {
        "name": "HAPU",
        "author": [
          { "name": "inket", "link": "http://hummingbird.me/users/inket" },
        ],
        "form": "Desktop",
        "platform": "Mac",
        "screenshots": [sb+"hapu/hapu-1.jpg"],
        "desc": "HAPU is a mac menubar app that allows you to interact with Hummingbird and automatically update your anime progress.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/mac-hapu-scrobbler-app/56" },
          { "name": "Download", "link": "http://tars.mahdi.jp/squirrel/hapu.zip" },
        ]
      },
      {
        "name": "Anitro Mobile",
        "author": [
          { "name": "Killerrin", "link": "http://hummingbird.me/users/Killerrin" },
        ],
        "form": "Mobile",
        "platform": "Windows Phone",
        "screenshots": [sb+"anitro-mobile/anitro-mobile-1.jpg", sb+"anitro-mobile/anitro-mobile-1.jpg"],
        "desc": "Anitro is an application for Windows Phone 8.1 that allows you to View and Manage your entire profile.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/anitro-windows-8-1-windows-phone-8-1/3824" },
          { "name": "Download", "link": "http://www.windowsphone.com/en-us/store/app/anitro/6377b104-7df2-4d29-8ebb-88527728e673" },
        ]
      },
      {
        "name": "Anitro",
        "author": [
          { "name": "Killerrin", "link": "http://hummingbird.me/users/Killerrin" },
        ],
        "form": "Desktop",
        "platform": "Windows",
        "screenshots": [sb+"anitro/anitro-1.jpg"],
        "desc": "Anitro is an application for Windows 8.1 that allows you to View and Manage your entire profile.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/anitro-windows-8-1-windows-phone-8-1/3824" },
          { "name": "Download", "link": "http://apps.microsoft.com/windows/en-us/app/anitro/665e06bf-b4de-4cde-8ae3-c20c1a98b12b" },
        ]
      },
      {
        "name": "Hummingbird Updater",
        "author": [
          { "name": "Dennis", "link": "http://hummingbird.me/users/Dennis" },
        ],
        "form": "Desktop",
        "platform": "Windows",
        "screenshots": [sb+"hb-updater/topkek.jpg"],
        "desc": "Hummingbird Updater is a tiny Hummingbird scrobbler that automatically updates your list whenever VLC/MPC is playing your favorite show.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/windows-hummingbird-updater-scrobbler-by-dennis/17333" },
          { "name": "Download", "link": "https://github.com/Desuvader/Hummingbird-Updater/releases/latest" },
        ]
      },
      {
        "name": "Deadman",
        "author": [
          { "name": "Jigglyslime", "link": "https://hummingbird.me/users/Jigglyslime" },
        ],
        "form": "Mobile",
        "platform": "iOS",
        "screenshots": [sb+"deadman/deadman-1.jpg", sb+"deadman/deadman-2.jpg"],
        "desc": "It's like Hummingbird, but iOS.  And native.  Makes love to your face. Enjoy.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/ios-deadman/20315" },
          { "name": "Download", "link": "https://itunes.apple.com/us/app/deadman-for-hummingbird/id943835088?ls=1&mt=8" },
        ]
      },
      {
  	    "name": "Hachidori",
  	    "author": [
  	      { "name": "chikorita157", "link": "https://hummingbird.me/users/chikorita157" },
  	    ],
  	    "form": "Desktop",
  	    "platform": "Mac",
  	    "screenshots": [sb+"hachidori/hachidori.jpg"],
  	    "desc": "Hachidori is a lightweight and fast Hummingbird scrobbler that automatically updates your list from popular media players (Mplayer, VLC, Mpv), legal streaming sites and Plex Media Server.",
  	    "links": [
  	      { "name": "Discuss", "link": "https://forums.hummingbird.me/t/mac-hachidori-open-source-hummingbird-scrobbler-for-os-x/16952" },
  	      { "name": "Download", "link": "http://hachidori.ateliershiori.moe/download.php" },
  	    ]
      },
      {
  	    "name": "",
  	    "author": [
  	      { "name": "z411", "link": "https://hummingbird.me/users/z411" },
  	    ],
  	    "form": "Desktop",
  	    "platform": "Linux",
  	    "screenshots": [sb+"trackma/trackma.jpg"],
  	    "desc": "Trackma aims to be a lightweight and simple but feature-rich program for Unix based systems for fetching, updating and using data from personal lists hosted in several media tracking websites.",
  	    "links": [
  	      { "name": "Discuss", "link": "https://forums.hummingbird.me/t/linux-trackma/19212" },
  	      { "name": "Download", "link": "https://z411.github.io/trackma/" },
  	    ]
      }
    ];
  }
});
<commit_msg>That never hapened<commit_after>import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    // Screenshot base asset directory
    var sb = "/assets/app-screens/";

    // The apps page only lists actively supported, community made apps.
    // Since this list is mostly static, it will be hardcoded as a model.
    return [
      {
        "name": "Tenpenchii",
        "author": [
          { "name": "Tempest", "link": "http://hummingbird.me/users/Tempest" },
        ],
        "form": "Mobile",
        "platform": "Android",
        "screenshots": [sb+"tenpenchii/tenpenchii-1.jpg", sb+"tenpenchii/tenpenchii-2.jpg"],
        "desc": "Tenpenchii is a Hummingbird client for Android devices with a modern user interface and amazing functionality.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/android-tenpenchii/582" },
          { "name": "Download", "link": "https://play.google.com/store/apps/details?id=com.fractalemagic" },
        ]
      },
      {
        "name": "HummingList",
        "author": [
          { "name": "Riddle", "link": "http://hummingbird.me/users/riddle" },
        ],
        "form": "Mobile",
        "platform": "iOS",
        "screenshots": [sb+"humminglist/humminglist-1.jpg", sb+"humminglist/humminglist-2.jpg"],
        "desc": "Manage your Hummingbird library on the go from your iPhone, iPad or iPod touch.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/humminglist-ios-app/17003" },
          { "name": "Download", "link": "https://itunes.apple.com/us/app/humminglist/id866713967?mt=8" },
        ]
      },
      {
        "name": "Taiga",
        "author": [
          { "name": "Erengy", "link": "http://hummingbird.me/users/Erengy" },
        ],
        "form": "Desktop",
        "platform": "Windows",
        "screenshots": [sb+"taiga/taiga-1.jpg"],
        "desc": "Taiga helps you manage your list, discover new series, share watched episodes and download new ones.",
        "links": [
          { "name": "Discuss", "link": "http://forums.hummingbird.me/t/windows-taiga/10565" },
          { "name": "Download", "link": "http://taiga.erengy.com/download.php" },
        ]
      },
      {
        "name": "HAPU",
        "author": [
          { "name": "inket", "link": "http://hummingbird.me/users/inket" },
        ],
        "form": "Desktop",
        "platform": "Mac",
        "screenshots": [sb+"hapu/hapu-1.jpg"],
        "desc": "HAPU is a mac menubar app that allows you to interact with Hummingbird and automatically update your anime progress.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/mac-hapu-scrobbler-app/56" },
          { "name": "Download", "link": "http://tars.mahdi.jp/squirrel/hapu.zip" },
        ]
      },
      {
        "name": "Anitro Mobile",
        "author": [
          { "name": "Killerrin", "link": "http://hummingbird.me/users/Killerrin" },
        ],
        "form": "Mobile",
        "platform": "Windows Phone",
        "screenshots": [sb+"anitro-mobile/anitro-mobile-1.jpg", sb+"anitro-mobile/anitro-mobile-1.jpg"],
        "desc": "Anitro is an application for Windows Phone 8.1 that allows you to View and Manage your entire profile.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/anitro-windows-8-1-windows-phone-8-1/3824" },
          { "name": "Download", "link": "http://www.windowsphone.com/en-us/store/app/anitro/6377b104-7df2-4d29-8ebb-88527728e673" },
        ]
      },
      {
        "name": "Anitro",
        "author": [
          { "name": "Killerrin", "link": "http://hummingbird.me/users/Killerrin" },
        ],
        "form": "Desktop",
        "platform": "Windows",
        "screenshots": [sb+"anitro/anitro-1.jpg"],
        "desc": "Anitro is an application for Windows 8.1 that allows you to View and Manage your entire profile.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/anitro-windows-8-1-windows-phone-8-1/3824" },
          { "name": "Download", "link": "http://apps.microsoft.com/windows/en-us/app/anitro/665e06bf-b4de-4cde-8ae3-c20c1a98b12b" },
        ]
      },
      {
        "name": "Hummingbird Updater",
        "author": [
          { "name": "Dennis", "link": "http://hummingbird.me/users/Dennis" },
        ],
        "form": "Desktop",
        "platform": "Windows",
        "screenshots": [sb+"hb-updater/topkek.jpg"],
        "desc": "Hummingbird Updater is a tiny Hummingbird scrobbler that automatically updates your list whenever VLC/MPC is playing your favorite show.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/windows-hummingbird-updater-scrobbler-by-dennis/17333" },
          { "name": "Download", "link": "https://github.com/Desuvader/Hummingbird-Updater/releases/latest" },
        ]
      },
      {
        "name": "Deadman",
        "author": [
          { "name": "Jigglyslime", "link": "https://hummingbird.me/users/Jigglyslime" },
        ],
        "form": "Mobile",
        "platform": "iOS",
        "screenshots": [sb+"deadman/deadman-1.jpg", sb+"deadman/deadman-2.jpg"],
        "desc": "It's like Hummingbird, but iOS.  And native.  Makes love to your face. Enjoy.",
        "links": [
          { "name": "Discuss", "link": "https://forums.hummingbird.me/t/ios-deadman/20315" },
          { "name": "Download", "link": "https://itunes.apple.com/us/app/deadman-for-hummingbird/id943835088?ls=1&mt=8" },
        ]
      },
      {
  	    "name": "Hachidori",
  	    "author": [
  	      { "name": "chikorita157", "link": "https://hummingbird.me/users/chikorita157" },
  	    ],
  	    "form": "Desktop",
  	    "platform": "Mac",
  	    "screenshots": [sb+"hachidori/hachidori.jpg"],
  	    "desc": "Hachidori is a lightweight and fast Hummingbird scrobbler that automatically updates your list from popular media players (Mplayer, VLC, Mpv), legal streaming sites and Plex Media Server.",
  	    "links": [
  	      { "name": "Discuss", "link": "https://forums.hummingbird.me/t/mac-hachidori-open-source-hummingbird-scrobbler-for-os-x/16952" },
  	      { "name": "Download", "link": "http://hachidori.ateliershiori.moe/download.php" },
  	    ]
      },
      {
  	    "name": "Trackma",
  	    "author": [
  	      { "name": "z411", "link": "https://hummingbird.me/users/z411" },
  	    ],
  	    "form": "Desktop",
  	    "platform": "Linux",
  	    "screenshots": [sb+"trackma/trackma.jpg"],
  	    "desc": "Trackma aims to be a lightweight and simple but feature-rich program for Unix based systems for fetching, updating and using data from personal lists hosted in several media tracking websites.",
  	    "links": [
  	      { "name": "Discuss", "link": "https://forums.hummingbird.me/t/linux-trackma/19212" },
  	      { "name": "Download", "link": "https://z411.github.io/trackma/" },
  	    ]
      }
    ];
  }
});
<|endoftext|>