<commit_before>/* eslint no-unused-expressions:0 */

window.requestAnimationFrame = callback => callback();  // make it synchronous for testing

import React from 'react';
import ReactDOM from 'react-dom';

import UITable from '../index';
import conformanceChecker from '../../UIUtils/conform';
import noop from '../../UIUtils/noop';

import sinon from 'sinon';
import {times} from 'lodash';

const rows = [{"id":1,"first_name":"Louise","last_name":"Fernandez","job_title":"Database Administrator I","phone":"6-(697)972-8601","email":"lfernandez1@opera.com","address1":"5049 Barnett Road","city":"Nglengkir","country":"Indonesia","country_code":"ID"},{"id":2,"first_name":"Dennis","last_name":"Nichols","job_title":"Nurse","phone":"9-(896)552-6623","email":"dnichols0@ycombinator.com","address1":"0 Drewry Drive","city":"Canggetelo","country":"Indonesia","country_code":"ID"},{"id":3,"first_name":"Stephen","last_name":"Hamilton","job_title":"Dental Hygienist","phone":"1-(274)517-4270","email":"shamilton2@amazon.co.jp","address1":"11 David Crossing","city":"Kotabaru","country":"Indonesia","country_code":"ID"},{"id":4,"first_name":"Shawn","last_name":"Richards","job_title":"Librarian","phone":"1-(173)205-8062","email":"srichards3@4shared.com","address1":"47533 Sherman Street","city":"Viengxay","country":"Laos","country_code":"LA"},{"id":5,"first_name":"John","last_name":"Hansen","job_title":"Staff Scientist","phone":"5-(650)401-5661","email":"jhansen4@sfgate.com","address1":"955 Jackson Park","city":"South Tangerang","country":"Indonesia","country_code":"ID"},{"id":6,"first_name":"Ronald","last_name":"Alexander","job_title":"Structural Engineer","phone":"7-(675)732-2723","email":"ralexander5@usgs.gov","address1":"0858 Hooker Court","city":"Kardítsa","country":"Greece","country_code":"GR"},{"id":7,"first_name":"Shawn","last_name":"Myers","job_title":"Executive Secretary","phone":"0-(903)830-7054","email":"smyers6@addtoany.com","address1":"69605 Rusk Junction","city":"Erpeldange","country":"Luxembourg","country_code":"LU"},{"id":8,"first_name":"Andrew","last_name":"Hill","job_title":"Research Nurse","phone":"9-(825)250-8207","email":"ahill7@sohu.com","address1":"4 Lunder Junction","city":"Naji","country":"China","country_code":"CN"},{"id":9,"first_name":"Susan","last_name":"Fowler","job_title":"Product Engineer","phone":"2-(891)897-3096","email":"sfowler8@addtoany.com","address1":"17 Artisan Pass","city":"Oslomej","country":"Macedonia","country_code":"MK"},{"id":10,"first_name":"Denise","last_name":"Gonzalez","job_title":"Associate Professor","phone":"7-(665)859-5877","email":"dgonzalez9@answers.com","address1":"8538 Sage Hill","city":"Baiima","country":"Sierra Leone","country_code":"SL"}];

// index 3 is for the ui-table-row-loading css hook test
const rowGetter = index => index === 3 ? new Promise(noop) : rows[index];

const rowsAlt = [{"id":1,"first_name":"Lana","last_name":"Fernandez","job_title":"Database Administrator I","phone":"6-(697)972-8601","email":"lfernandez1@opera.com","address1":"5049 Barnett Road","city":"Nglengkir","country":"Indonesia","country_code":"ID"}];

const altRowGetter = index => rowsAlt[index];

const columns = [{title:'FirstName',mapping:'first_name',resizable:true,width: 100},{title:'LastName',mapping:'last_name',resizable:true,width: 100},{width:100,title:'JobTitle',mapping:'job_title',resizable:true},{title:'Phone',mapping:'phone',resizable:true,width: 100},{title:'EmailAddress',mapping:'email',resizable:true,width: 100},{title:'StreetAddress',mapping:'address1',resizable:true,width: 100},{title:'City',mapping:'city',resizable:true,width: 100},{title:'Country',mapping:'country',resizable:true,width: 100},{title:'CountryCode',mapping:'country_code',resizable:true,width: 100}];

const baseProps = {
    identifier: 'foo',
    getRow: rowGetter,
    columns: columns,
    totalRows: rows.length,
};

describe('UITable', () => {
    const mountNode = document.body.appendChild(document.createElement('div'));
    const render = vdom => ReactDOM.render(vdom, mountNode);

    const sandbox = sinon.sandbox.create();

    afterEach(() => {
        ReactDOM.unmountComponentAtNode(mountNode);
        sandbox.restore();
    });

    it('conforms to the UIKit prop interface standards', () => conformanceChecker(render, UITable, baseProps));

    describe('CSS hooks', () => {
        let element;

        beforeEach(() => element = render(<UITable {...baseProps} />));

        it('ui-table-wrapper is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.className).toContain('ui-table-wrapper');
        });

        it('ui-table-body is rendered', () => {
            expect(element.refs.body.className).toContain('ui-table-body');
        });

        it('ui-table-row is rendered', () => {
            const node = element.refs.body;

            expect(node.querySelector('.ui-table-row')).not.toBe(null);
        });

        it('ui-table-row-even is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-row-even')).not.toBe(null);
        });

        it('ui-table-row-odd is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-row-odd')).not.toBe(null);
        });

        it('ui-table-row-loading is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-row-loading')).not.toBe(null);
        });

        it('ui-table-cell is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-cell')).not.toBe(null);
        });

        it('ui-table-cell-even is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-cell-even')).not.toBe(null);
        });

        it('ui-table-cell-odd is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-cell-odd')).not.toBe(null);
        });

        it('ui-table-header-cell is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-header-cell')).not.toBe(null);
        });

        it('ui-table-header-cell-resize-handle is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-header-cell-resize-handle')).not.toBe(null);
        });

        it('ui-table-x-scroll-track is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-x-scroll-track')).not.toBe(null);
        });

        it('ui-table-x-scroll-handle is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-x-scroll-handle')).not.toBe(null);
        });

        it('ui-table-y-scroll-track is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-y-scroll-track')).not.toBe(null);
        });

        it('ui-table-y-scroll-handle is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-y-scroll-handle')).not.toBe(null);
        });
    });

    describe('props.identifier', () => {
        it('fully resets the table when changed', () => {
            let element = render(<UITable {...baseProps} />);

            expect(element.refs.body.querySelector('.ui-table-cell').textContent).toBe('Louise');

            element = render(
                <UITable {...baseProps} identifier='alternate' totalRows={rowsAlt.length} getRow={altRowGetter} />
            );

            expect(element.refs.body.querySelector('.ui-table-cell').textContent).toBe('Lana');
        });
    });

    describe('regeneration', () => {
        it('does not occur if nothing changed', () => {
            let element;

            element = render(<UITable {...baseProps} />);

            sandbox.spy(element.table, 'regenerate');

            element = render(<UITable {...baseProps} />);
            expect(element.table.regenerate.called).toBe(false);
        });

        it('occurs if a column is changed', () => {
            let element;

            element = render(<UITable {...baseProps} />);

            sandbox.spy(element.table, 'regenerate');

            element.table.columns[0].width = 300;

            const modified_columns = [
                {...baseProps.columns[0], width: 299},
                ...baseProps.columns.slice(1),
            ];

            element = render(<UITable {...baseProps} columns={modified_columns} />);
            expect(element.table.regenerate.calledOnce).toBe(true);
        });

        it('does not occur if a column width is changed to match the internal column width', () => {
            let element;

            element = render(<UITable {...baseProps} />);

            sandbox.spy(element.table, 'regenerate');

            element.table.columns[0].width = 300;

            const modified_columns = [
                {...baseProps.columns[0], width: 300},
                ...baseProps.columns.slice(1),
            ];

            element = render(<UITable {...baseProps} columns={modified_columns} />);
            expect(element.table.regenerate.called).toBe(false);
        });

        it('only occurs once when given `props.jumpToRowIndex`', () => {
            let element;

            element = render(<UITable {...baseProps} />);

            sandbox.spy(element.table, 'regenerate');

            element = render(<UITable {...baseProps} jumpToRowIndex={1} />);
            expect(element.table.regenerate.calledOnce).toBe(true);
        });
    });
});
<commit_msg>Revert UITable test change to set a baseline width for everything<commit_after>/* eslint no-unused-expressions:0 */

window.requestAnimationFrame = callback => callback();  // make it synchronous for testing

import React from 'react';
import ReactDOM from 'react-dom';

import UITable from '../index';
import conformanceChecker from '../../UIUtils/conform';
import noop from '../../UIUtils/noop';

import sinon from 'sinon';
import {times} from 'lodash';

const rows = [{"id":1,"first_name":"Louise","last_name":"Fernandez","job_title":"Database Administrator I","phone":"6-(697)972-8601","email":"lfernandez1@opera.com","address1":"5049 Barnett Road","city":"Nglengkir","country":"Indonesia","country_code":"ID"},{"id":2,"first_name":"Dennis","last_name":"Nichols","job_title":"Nurse","phone":"9-(896)552-6623","email":"dnichols0@ycombinator.com","address1":"0 Drewry Drive","city":"Canggetelo","country":"Indonesia","country_code":"ID"},{"id":3,"first_name":"Stephen","last_name":"Hamilton","job_title":"Dental Hygienist","phone":"1-(274)517-4270","email":"shamilton2@amazon.co.jp","address1":"11 David Crossing","city":"Kotabaru","country":"Indonesia","country_code":"ID"},{"id":4,"first_name":"Shawn","last_name":"Richards","job_title":"Librarian","phone":"1-(173)205-8062","email":"srichards3@4shared.com","address1":"47533 Sherman Street","city":"Viengxay","country":"Laos","country_code":"LA"},{"id":5,"first_name":"John","last_name":"Hansen","job_title":"Staff Scientist","phone":"5-(650)401-5661","email":"jhansen4@sfgate.com","address1":"955 Jackson Park","city":"South Tangerang","country":"Indonesia","country_code":"ID"},{"id":6,"first_name":"Ronald","last_name":"Alexander","job_title":"Structural Engineer","phone":"7-(675)732-2723","email":"ralexander5@usgs.gov","address1":"0858 Hooker Court","city":"Kardítsa","country":"Greece","country_code":"GR"},{"id":7,"first_name":"Shawn","last_name":"Myers","job_title":"Executive Secretary","phone":"0-(903)830-7054","email":"smyers6@addtoany.com","address1":"69605 Rusk Junction","city":"Erpeldange","country":"Luxembourg","country_code":"LU"},{"id":8,"first_name":"Andrew","last_name":"Hill","job_title":"Research Nurse","phone":"9-(825)250-8207","email":"ahill7@sohu.com","address1":"4 Lunder Junction","city":"Naji","country":"China","country_code":"CN"},{"id":9,"first_name":"Susan","last_name":"Fowler","job_title":"Product Engineer","phone":"2-(891)897-3096","email":"sfowler8@addtoany.com","address1":"17 Artisan Pass","city":"Oslomej","country":"Macedonia","country_code":"MK"},{"id":10,"first_name":"Denise","last_name":"Gonzalez","job_title":"Associate Professor","phone":"7-(665)859-5877","email":"dgonzalez9@answers.com","address1":"8538 Sage Hill","city":"Baiima","country":"Sierra Leone","country_code":"SL"}];

// index 3 is for the ui-table-row-loading css hook test
const rowGetter = index => index === 3 ? new Promise(noop) : rows[index];

const rowsAlt = [{"id":1,"first_name":"Lana","last_name":"Fernandez","job_title":"Database Administrator I","phone":"6-(697)972-8601","email":"lfernandez1@opera.com","address1":"5049 Barnett Road","city":"Nglengkir","country":"Indonesia","country_code":"ID"}];

const altRowGetter = index => rowsAlt[index];

const columns = [{title:'FirstName',mapping:'first_name',resizable:true},{title:'LastName',mapping:'last_name',resizable:true},{width:100,title:'JobTitle',mapping:'job_title',resizable:true},{title:'Phone',mapping:'phone',resizable:true},{title:'EmailAddress',mapping:'email',resizable:true},{title:'StreetAddress',mapping:'address1',resizable:true},{title:'City',mapping:'city',resizable:true},{title:'Country',mapping:'country',resizable:true},{title:'CountryCode',mapping:'country_code',resizable:true}];

const baseProps = {
    identifier: 'foo',
    getRow: rowGetter,
    columns: columns,
    totalRows: rows.length,
};

describe('UITable', () => {
    const mountNode = document.body.appendChild(document.createElement('div'));
    const render = vdom => ReactDOM.render(vdom, mountNode);

    const sandbox = sinon.sandbox.create();

    afterEach(() => {
        ReactDOM.unmountComponentAtNode(mountNode);
        sandbox.restore();
    });

    it('conforms to the UIKit prop interface standards', () => conformanceChecker(render, UITable, baseProps));

    describe('CSS hooks', () => {
        let element;

        beforeEach(() => element = render(<UITable {...baseProps} />));

        it('ui-table-wrapper is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.className).toContain('ui-table-wrapper');
        });

        it('ui-table-body is rendered', () => {
            expect(element.refs.body.className).toContain('ui-table-body');
        });

        it('ui-table-row is rendered', () => {
            const node = element.refs.body;

            expect(node.querySelector('.ui-table-row')).not.toBe(null);
        });

        it('ui-table-row-even is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-row-even')).not.toBe(null);
        });

        it('ui-table-row-odd is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-row-odd')).not.toBe(null);
        });

        it('ui-table-row-loading is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-row-loading')).not.toBe(null);
        });

        it('ui-table-cell is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-cell')).not.toBe(null);
        });

        it('ui-table-cell-even is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-cell-even')).not.toBe(null);
        });

        it('ui-table-cell-odd is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-cell-odd')).not.toBe(null);
        });

        it('ui-table-header-cell is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-header-cell')).not.toBe(null);
        });

        it('ui-table-header-cell-resize-handle is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-header-cell-resize-handle')).not.toBe(null);
        });

        it('ui-table-x-scroll-track is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-x-scroll-track')).not.toBe(null);
        });

        it('ui-table-x-scroll-handle is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-x-scroll-handle')).not.toBe(null);
        });

        it('ui-table-y-scroll-track is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-y-scroll-track')).not.toBe(null);
        });

        it('ui-table-y-scroll-handle is rendered', () => {
            const node = element.refs.wrapper;

            expect(node.querySelector('.ui-table-y-scroll-handle')).not.toBe(null);
        });
    });

    describe('props.identifier', () => {
        it('fully resets the table when changed', () => {
            let element = render(<UITable {...baseProps} />);

            expect(element.refs.body.querySelector('.ui-table-cell').textContent).toBe('Louise');

            element = render(
                <UITable {...baseProps} identifier='alternate' totalRows={rowsAlt.length} getRow={altRowGetter} />
            );

            expect(element.refs.body.querySelector('.ui-table-cell').textContent).toBe('Lana');
        });
    });

    describe('regeneration', () => {
        it('does not occur if nothing changed', () => {
            let element;

            element = render(<UITable {...baseProps} />);

            sandbox.spy(element.table, 'regenerate');

            element = render(<UITable {...baseProps} />);
            expect(element.table.regenerate.called).toBe(false);
        });

        it('occurs if a column is changed', () => {
            let element;

            element = render(<UITable {...baseProps} />);

            sandbox.spy(element.table, 'regenerate');

            element.table.columns[0].width = 300;

            const modified_columns = [
                {...baseProps.columns[0], width: 299},
                ...baseProps.columns.slice(1),
            ];

            element = render(<UITable {...baseProps} columns={modified_columns} />);
            expect(element.table.regenerate.calledOnce).toBe(true);
        });

        it('does not occur if a column width is changed to match the internal column width', () => {
            let element;

            element = render(<UITable {...baseProps} />);

            sandbox.spy(element.table, 'regenerate');

            element.table.columns[0].width = 300;

            const modified_columns = [
                {...baseProps.columns[0], width: 300},
                ...baseProps.columns.slice(1),
            ];

            element = render(<UITable {...baseProps} columns={modified_columns} />);
            expect(element.table.regenerate.called).toBe(false);
        });

        it('only occurs once when given `props.jumpToRowIndex`', () => {
            let element;

            element = render(<UITable {...baseProps} />);

            sandbox.spy(element.table, 'regenerate');

            element = render(<UITable {...baseProps} jumpToRowIndex={1} />);
            expect(element.table.regenerate.calledOnce).toBe(true);
        });
    });
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
YUI().use('mojito-test-extra', 'test', function(Y) {

    var A = Y.Assert,
        AA = Y.ArrayAssert,
        OA = Y.ObjectAssert,
        cases,

        shared = require(Y.MOJITO_DIR + 'lib/app/commands/build/shared.js'),
        count,
        conf;

    conf = {
        mojitodir: '/Users/isao/Repos/mojito/myfork/',
        app: {
            name: 'staticpf',
            version: '0.0.1',
            specs: {
                frame: {},
                tunnelProxy: {}
            },
            dir: '/path/to/app'
        },
        snapshot: {
            name: '',
            tag: '',
            packages: {}
        },
        build: {
            attachManifest: false,
            forceRelativePaths: false,
            insertCharset: 'UTF-8',
            port: 1111,
            dir: '/path/to/build/dir',
            type: 'html5app',
            uris: []
        },
        context: {
            device: 'iphone'
        },
        contextqs: '?device=iphone',
        tunnelpf: '/tunnel',
        staticpf: 'staticpf'
    };

    cases = {
        name: 'build/shared cases',
        setUp: function() {
            count = 0;
            shared.init({
                copydir: function(file) { count++; return file; },
                copy: function(from, to) { count++; },
                write: function(file, str) { count++; },
            });
        },
        tearDown: function() {},

        'test init': function () {
            A.areSame(0, count);
            A.areSame(undefined, shared.init({}));
            A.areSame(0, count);
        },

        'test copyModule': function () {
            A.areSame(0, count);
            shared.copyModule('package/dir', 'module_name', 'dest/dir');
            A.areSame(1, count);
        },

        'test mapStoreUris modifies buildmap param by reference': function () {
            var buildmap = {},
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css'
                },
                expected = {
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css'
                };

            shared.mapStoreUris(buildmap, conf, storemap);
            OA.areEqual(expected, buildmap);
        },

        'test mapStoreUris maps css, js, json files': function () {
            var buildmap = {
                    '/?device=iphone': '/index.html'
                },
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js': '/path/to/app/mojits/top_frame/binders/index.js',
                    '/staticpf/top_frame/package.json': '/path/to/app/mojits/top_frame/package.json',
                    '/staticpf/top_frame/views/index.hb.html': '/path/to/app/mojits/top_frame/views/index.hb.html',
                    '/favicon.ico': '/Users/isao/Repos/mojito/myfork/lib/app/assets/favicon.ico',
                },
                expected = {
                    '/?device=iphone': '/index.html',
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js?device=iphone': '/staticpf/top_frameBinderIndex.js',
                    '/staticpf/top_frame/package.json?device=iphone': '/staticpf/top_frame/package.json'
                };

            shared.mapStoreUris(buildmap, conf, storemap);
            OA.areEqual(expected, buildmap);
        },

        'test mapStoreUris maps copies html, favicon, etc files to build dir': function () {
            var buildmap = {},
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js': '/path/to/app/mojits/top_frame/binders/index.js',
                    '/staticpf/top_frame/package.json': '/path/to/app/mojits/top_frame/package.json',
                    '/staticpf/top_frame/views/index.hb.html': '/path/to/app/mojits/top_frame/views/index.hb.html',
                    '/favicon.ico': '/Users/isao/Repos/mojito/myfork/lib/app/assets/favicon.ico',
                },
                expected = {
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js?device=iphone': '/staticpf/top_frameBinderIndex.js',
                    '/staticpf/top_frame/package.json?device=iphone': '/staticpf/top_frame/package.json'
                };

            A.areSame(0, count);
            shared.mapStoreUris(buildmap, conf, storemap);
            OA.areEqual(expected, buildmap);
            A.areSame(2, count);
        },

        'test mapDefxUris adds definition.json for client mojits': function () {
            var mojits = [
                    {source: {
                        fs: {
                            fullPath: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootDir: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootType: 'app',
                            subDir: '.',
                            subDirArray: [ '.' ],
                            isFile: false,
                            ext: '',
                            basename: ''
                        },
                        pkg: { name: 'yahoo.application.test50', version: '0.0.1', depth: 0 } },
                        type: 'mojit',
                        name: 'top_frame',
                        id: 'mojit--top_frame',
                        affinity: { affinity: 'common' },
                        selector: '*',
                        mime: { type: 'application/octet-stream', charset: undefined },
                        url: '/yahoo.application.test50/top_frame'
                    }
                ],
                store = {
                    getResources: function(type, ctx, filter) {return mojits;}
                },
                buildmap = {},
                expected = {'/tunnel/yahoo.application.test50/top_frame/definition.json?device=iphone': '/yahoo.application.test50/top_frame/definition.json'};

            A.areSame(0, count);
            shared.mapDefxUris(buildmap, conf, store);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapDefxUris does nothing if mojit has no "url"': function () {
            var mojits = [
                    {source: {
                        fs: {
                            fullPath: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootDir: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootType: 'app',
                            subDir: '.',
                            subDirArray: [ '.' ],
                            isFile: false,
                            ext: '',
                            basename: ''
                        },
                        pkg: { name: 'yahoo.application.test50', version: '0.0.1', depth: 0 } },
                        type: 'mojit',
                        name: 'top_frame',
                        id: 'mojit--top_frame',
                        affinity: { affinity: 'common' },
                        selector: '*',
                        mime: { type: 'application/octet-stream', charset: undefined },
                        REDACTED_URL: '/yahoo.application.test50/top_frame'
                    }
                ],
                store = {
                    getResources: function(type, ctx, filter) {return mojits;}
                },
                buildmap = {},
                expected = {};

            A.areSame(0, count);
            shared.mapDefxUris(buildmap, conf, store);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapFunkySpecUris maps app.json specs to default.json files': function () {
            var buildmap = {},
                expected = {
                    '/tunnel/staticpf/frame/specs/default.json?device=iphone': '/staticpf/frame/specs/default.json',
                    '/tunnel/staticpf/tunnelProxy/specs/default.json?device=iphone': '/staticpf/tunnelProxy/specs/default.json'
                };

            A.areSame(0, count);
            shared.mapFunkySpecUris(buildmap, conf);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapFunkySpecUris handles obscure Livestand mojit specs': function () {
            var buildmap = {},
                expected = {'tunnelpf/staticpf/foo/specs/bar.jsonundefined': '/staticpf/foo/specs/bar.json'};

            A.areSame(0, count);
            shared.mapFunkySpecUris(buildmap, {staticpf:'staticpf', tunnelpf:'tunnelpf',app:{specs:{'foo:bar':{}}}});

            console.log('-----------');
            console.log(buildmap);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test makeManifest writes a file': function () {
            var buildmap = buildmap = {
                    '/?device=iphone': '/index.html'
                };

            A.areSame(0, count);
            shared.makeManifest(buildmap, 'build/dir', new Date());
            A.areSame(1, count);
        },

        'test mungePage does nothing unless attachManifest, insertCharset, forceRelativePaths are true': function () {
            var uri = '/uri.html',
                oldstr = 'blah blah <html> blah blah',
                newstr;

            A.areSame(0, count);
            newstr = shared.mungePage(conf, uri, oldstr);
            A.areSame(newstr, oldstr);
            A.areSame(1, count);
        },

        'test mungePage does nothing if uri is not *.html': function () {
            var uri = '/uri.css',
                oldstr = 'blah blah <html> blah blah',
                newstr,
                expected = 'blah blah <html manifest="some/uri"> blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areSame(newstr, oldstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for attachManifest:true': function () {
            var uri = '/uri.html',
                oldstr = 'blah blah <html> blah blah',
                newstr,
                expected = 'blah blah <html manifest="cache.manifest"> blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for attachManifest:true can apply relative path': function () {
            var uri = '/foo/bar/uri.html',
                oldstr = 'blah blah <html> blah blah',
                newstr,
                expected = 'blah blah <html manifest="../../cache.manifest"> blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },
    };

    Y.Test.Runner.add(new Y.Test.Case(cases));
});
<commit_msg>complete tests for build/shared.js<commit_after>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
YUI().use('mojito-test-extra', 'test', function(Y) {

    var A = Y.Assert,
        AA = Y.ArrayAssert,
        OA = Y.ObjectAssert,
        cases,

        shared = require(Y.MOJITO_DIR + 'lib/app/commands/build/shared.js'),
        count,
        conf;

    conf = {
        mojitodir: '/Users/isao/Repos/mojito/myfork/',
        app: {
            name: 'staticpf',
            version: '0.0.1',
            specs: {
                frame: {},
                tunnelProxy: {}
            },
            dir: '/path/to/app'
        },
        snapshot: {
            name: '',
            tag: '',
            packages: {}
        },
        build: {
            attachManifest: false,
            forceRelativePaths: false,
            insertCharset: 'UTF-8',
            port: 1111,
            dir: '/path/to/build/dir',
            type: 'html5app',
            uris: []
        },
        context: {
            device: 'iphone'
        },
        contextqs: '?device=iphone',
        tunnelpf: '/tunnel',
        staticpf: 'staticpf'
    };

    cases = {
        name: 'build/shared cases',
        setUp: function() {
            count = 0;
            shared.init({
                copydir: function(file) { count++; return file; },
                copy: function(from, to) { count++; },
                write: function(file, str) { count++; },
            });
        },
        tearDown: function() {},

        'test init': function () {
            A.areSame(0, count);
            A.areSame(undefined, shared.init({}));
            A.areSame(0, count);
        },

        'test copyModule': function () {
            A.areSame(0, count);
            shared.copyModule('package/dir', 'module_name', 'dest/dir');
            A.areSame(1, count);
        },

        'test mapStoreUris modifies buildmap param by reference': function () {
            var buildmap = {},
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css'
                },
                expected = {
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css'
                };

            shared.mapStoreUris(buildmap, conf, storemap);
            OA.areEqual(expected, buildmap);
        },

        'test mapStoreUris maps css, js, json files': function () {
            var buildmap = {
                    '/?device=iphone': '/index.html'
                },
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js': '/path/to/app/mojits/top_frame/binders/index.js',
                    '/staticpf/top_frame/package.json': '/path/to/app/mojits/top_frame/package.json',
                    '/staticpf/top_frame/views/index.hb.html': '/path/to/app/mojits/top_frame/views/index.hb.html',
                    '/favicon.ico': '/Users/isao/Repos/mojito/myfork/lib/app/assets/favicon.ico',
                },
                expected = {
                    '/?device=iphone': '/index.html',
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js?device=iphone': '/staticpf/top_frameBinderIndex.js',
                    '/staticpf/top_frame/package.json?device=iphone': '/staticpf/top_frame/package.json'
                };

            shared.mapStoreUris(buildmap, conf, storemap);
            OA.areEqual(expected, buildmap);
        },

        'test mapStoreUris maps copies html, favicon, etc files to build dir': function () {
            var buildmap = {},
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js': '/path/to/app/mojits/top_frame/binders/index.js',
                    '/staticpf/top_frame/package.json': '/path/to/app/mojits/top_frame/package.json',
                    '/staticpf/top_frame/views/index.hb.html': '/path/to/app/mojits/top_frame/views/index.hb.html',
                    '/favicon.ico': '/Users/isao/Repos/mojito/myfork/lib/app/assets/favicon.ico',
                },
                expected = {
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js?device=iphone': '/staticpf/top_frameBinderIndex.js',
                    '/staticpf/top_frame/package.json?device=iphone': '/staticpf/top_frame/package.json'
                };

            A.areSame(0, count);
            shared.mapStoreUris(buildmap, conf, storemap);
            OA.areEqual(expected, buildmap);
            A.areSame(2, count);
        },

        'test mapDefxUris adds definition.json for client mojits': function () {
            var mojits = [
                    {source: {
                        fs: {
                            fullPath: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootDir: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootType: 'app',
                            subDir: '.',
                            subDirArray: [ '.' ],
                            isFile: false,
                            ext: '',
                            basename: ''
                        },
                        pkg: { name: 'yahoo.application.test50', version: '0.0.1', depth: 0 } },
                        type: 'mojit',
                        name: 'top_frame',
                        id: 'mojit--top_frame',
                        affinity: { affinity: 'common' },
                        selector: '*',
                        mime: { type: 'application/octet-stream', charset: undefined },
                        url: '/yahoo.application.test50/top_frame'
                    }
                ],
                store = {
                    getResources: function(type, ctx, filter) {return mojits;}
                },
                buildmap = {},
                expected = {'/tunnel/yahoo.application.test50/top_frame/definition.json?device=iphone': '/yahoo.application.test50/top_frame/definition.json'};

            A.areSame(0, count);
            shared.mapDefxUris(buildmap, conf, store);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapDefxUris does nothing if mojit has no "url"': function () {
            var mojits = [
                    {source: {
                        fs: {
                            fullPath: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootDir: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootType: 'app',
                            subDir: '.',
                            subDirArray: [ '.' ],
                            isFile: false,
                            ext: '',
                            basename: ''
                        },
                        pkg: { name: 'yahoo.application.test50', version: '0.0.1', depth: 0 } },
                        type: 'mojit',
                        name: 'top_frame',
                        id: 'mojit--top_frame',
                        affinity: { affinity: 'common' },
                        selector: '*',
                        mime: { type: 'application/octet-stream', charset: undefined },
                        REDACTED_URL: '/yahoo.application.test50/top_frame'
                    }
                ],
                store = {
                    getResources: function(type, ctx, filter) {return mojits;}
                },
                buildmap = {},
                expected = {};

            A.areSame(0, count);
            shared.mapDefxUris(buildmap, conf, store);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapFunkySpecUris maps app.json specs to default.json files': function () {
            var buildmap = {},
                expected = {
                    '/tunnel/staticpf/frame/specs/default.json?device=iphone': '/staticpf/frame/specs/default.json',
                    '/tunnel/staticpf/tunnelProxy/specs/default.json?device=iphone': '/staticpf/tunnelProxy/specs/default.json'
                };

            A.areSame(0, count);
            shared.mapFunkySpecUris(buildmap, conf);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapFunkySpecUris handles obscure Livestand mojit specs': function () {
            var buildmap = {},
                expected = {'tunnelpf/staticpf/foo/specs/bar.json?foo=bar': '/staticpf/foo/specs/bar.json'};

            A.areSame(0, count);

            shared.mapFunkySpecUris(buildmap, {staticpf:'staticpf', contextqs: '?foo=bar', tunnelpf:'tunnelpf',app:{specs:{'foo:bar':{}}}});

            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test makeManifest writes a file': function () {
            var buildmap = buildmap = {
                    '/?device=iphone': '/index.html'
                };

            A.areSame(0, count);
            shared.makeManifest(buildmap, 'build/dir', new Date());
            A.areSame(1, count);
        },

        'test mungePage does nothing unless attachManifest, insertCharset, forceRelativePaths are true': function () {
            var uri = '/uri.html',
                oldstr = 'blah blah <html> blah blah',
                newstr;

            A.areSame(0, count);
            newstr = shared.mungePage(conf, uri, oldstr);
            A.areSame(newstr, oldstr);
            A.areSame(1, count);
        },

        'test mungePage does nothing if uri is not *.html': function () {
            var uri = '/uri.css',
                oldstr = 'blah blah <html> blah blah',
                newstr,
                expected = 'blah blah <html manifest="some/uri"> blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areSame(newstr, oldstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for attachManifest:true': function () {
            var uri = '/uri.html',
                oldstr = 'blah blah <html> blah blah',
                newstr,
                expected = 'blah blah <html manifest="cache.manifest"> blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for attachManifest:true can apply relative path': function () {
            var uri = '/foo/bar/uri.html',
                oldstr = 'blah blah <html> blah blah',
                newstr,
                expected = 'blah blah <html manifest="../../cache.manifest"> blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for forceRelativePaths:true': function () {
            var uri = '/foo/bar/uri.html',
                oldstr = 'blah blah <a href="/foo/bar/baz/bah.html"> blah blah',
                newstr,
                expected = 'blah blah <a href="baz/bah.html"> blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            uri = '/foo/bar/baz/bah/uri.html';
            oldstr = 'blah blah <a href="/foo/bah.html"> blah blah';
            expected = 'blah blah <a href="../../../bah.html"> blah blah';
            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(2, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for forceRelativePaths:true with no common root': function () {
            var uri = '/a/b/c/uri.html',
                oldstr = 'blah blah <a href="/foo/bar/baz/bah.html"> blah blah',
                newstr,
                expected = 'blah blah <a href="../../../foo/bar/baz/bah.html"> blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            uri = '/foo/bar/baz/bah/uri.html';
            oldstr = 'blah blah <a href="/foo/bah.html"> blah blah';
            expected = 'blah blah <a href="../../../bah.html"> blah blah';
            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(2, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for insertCharset:true': function () {
            var uri = '/',
                oldstr = 'blah blah <head beepoo="boppoo"\npoo> blah blah',
                newstr,
                expected = 'blah blah <head beepoo="boppoo"\npoo>\n<meta charset="UTF-8">\n blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for insertCharset:true simple tag': function () {
            var uri = '/',
                oldstr = 'blah blah <head> blah blah',
                newstr,
                expected = 'blah blah <head>\n<meta charset="UTF-8">\n blah blah',
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },

        'test mungePage for insertCharset:true does nothing if there already is a charset metatag': function () {
            var uri = '/',
                oldstr = 'blah blah <head boo\npoo> blah blah<meta charset="zippy">',
                newstr,
                oldbuildconf = conf.build;

            conf.build = {
                attachManifest: true,
                forceRelativePaths: true,
                insertCharset: 'UTF-8',
            };

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areSame(newstr, oldstr);
            A.areSame(1, count);

            conf.build = oldbuildconf;
        },

    };

    Y.Test.Runner.add(new Y.Test.Case(cases));
});
<|endoftext|>


<commit_before>/* global TrelloPowerUp */

var Promise = TrelloPowerUp.Promise;
var t = TrelloPowerUp.iframe();

t.render(function() {
  return t.get('board', 'shared', TEAM_TOKEN_KEY)
      .then(function(token) {
        if (token) {
          return geocodeAddress(token, address);
        } else {
          return t.overlay({
            url: './no-settings.html',
            args: {}
          })
          .then(function () {
            return t.closePopup();
          });
        }
      });
});

document.getElementById('save-location').addEventListener('click', function() {
  var addressTextArea = document.getElementById('address');
  var address = addressTextArea.value;
  var errorMessageElement = document.getElementById('error-message');
  errorMessageElement.innerHTML = '';
  if (address) {
    return t.get('board', 'shared', TEAM_TOKEN_KEY)
        .then(function(token) {
          return geocodeAddress(token, address);
        }).then(function(geocodeResult) {
          /*
          Example result:
           {"success":true,
           "data":{
              "inputAddress":"341 George St,Sydney",
              "geocodedLocation":{
                "latitude":-33.8672664,"longitude":151.2066123
              },
              "zeroResults":false,
              "errorOccurred":false
            }
          }
           */
          if (geocodeResult && geocodeResult.success && geocodeResult.data && geocodeResult.data.geocodedLocation) {
            var geocodedLocation = geocodeResult.data.geocodedLocation;
            var inputAddress = geocodeResult.data.inputAddress;
            return t.get('board', 'shared', TEAM_NAME_KEY)
            .then(function(teamName) {
                  var url = buildUrlWithDropPin(teamName, inputAddress, geocodedLocation.latitude, geocodedLocation.longitude);
                  return t.attach({
                    url: url,
                    name: inputAddress
                  })
                  .then(function(){
                    return t.closePopup();
                  });
            });
          } else {
            return t.closePopup();
          }
        }).then(function(sharedMapInfo) {
          var sharedMapInfoJson = JSON.stringify(sharedMapInfo);
          return t.set('board', 'shared', CACHED_SHARED_MAP_INFO_KEY, sharedMapInfoJson);
        }).then(function() {
          return t.closePopup();
        }).catch(function() {
          errorMessageElement.innerHTML = 'There was a problem getting information from Maprosoft. Check your settings and internet connection.';
        });
  } else {
    return t.closePopup();
  }
});
<commit_msg>update<commit_after>/* global TrelloPowerUp */

var Promise = TrelloPowerUp.Promise;
var t = TrelloPowerUp.iframe();

t.render(function() {
  var addressTextArea = document.getElementById('address');
  addressTextArea.focus();
  return t.get('board', 'shared', TEAM_TOKEN_KEY)
      .then(function(token) {
        if (token) {
          return geocodeAddress(token, address);
        } else {
          return t.overlay({
            url: './no-settings.html',
            args: {}
          })
          .then(function () {
            return t.closePopup();
          });
        }
      });
});

document.getElementById('save-location').addEventListener('click', function() {
  var addressTextArea = document.getElementById('address');
  var address = addressTextArea.value;
  var errorMessageElement = document.getElementById('error-message');
  errorMessageElement.innerHTML = '';
  if (address) {
    return t.get('board', 'shared', TEAM_TOKEN_KEY)
        .then(function(token) {
          return geocodeAddress(token, address);
        }).then(function(geocodeResult) {
          /*
          Example result:
           {"success":true,
           "data":{
              "inputAddress":"341 George St,Sydney",
              "geocodedLocation":{
                "latitude":-33.8672664,"longitude":151.2066123
              },
              "zeroResults":false,
              "errorOccurred":false
            }
          }
           */
          if (geocodeResult && geocodeResult.success && geocodeResult.data && geocodeResult.data.geocodedLocation) {
            var geocodedLocation = geocodeResult.data.geocodedLocation;
            var inputAddress = geocodeResult.data.inputAddress;
            return t.get('board', 'shared', TEAM_NAME_KEY)
            .then(function(teamName) {
                  var url = buildUrlWithDropPin(teamName, inputAddress, geocodedLocation.latitude, geocodedLocation.longitude);
                  return t.attach({
                    url: url,
                    name: inputAddress
                  })
                  .then(function(){
                    return t.closePopup();
                  });
            });
          } else {
            return t.closePopup();
          }
        }).then(function(sharedMapInfo) {
          var sharedMapInfoJson = JSON.stringify(sharedMapInfo);
          return t.set('board', 'shared', CACHED_SHARED_MAP_INFO_KEY, sharedMapInfoJson);
        }).then(function() {
          return t.closePopup();
        }).catch(function() {
          errorMessageElement.innerHTML = 'There was a problem getting information from Maprosoft. Check your settings and internet connection.';
        });
  } else {
    return t.closePopup();
  }
});
<|endoftext|>


<commit_before>;
define(['lib/sammy'], function (epl) {

/******************************
 * Contains app-wide settings *
 *****************************/
return {
	siteUrl : 'http://localhost:8000',
	viewDirectory : function () { return '/static/views'; },

	//API Keys
	apiKeys : {
		'google' : {
			'maps' : 'AIzaSyA59Z_Kym_voRl--cHJzYkep3Cs-_71'
		}
	},

	//Set up application routes
	routes : Sammy(function () {

		var self = this;

		//Example route
		self.get('#example', function () {
			//The template within /static/views/example.html will be loaded into the viewport provided to the AppClass constructor
			//@see the csc.View constructor for information on transitioning between views using the parameters below
			require(['epl', 'views'], function (epl, views) {
				epl.nav.transition(epl.views.example);
			});
		});

		//Route to Upload Story view
		this.get('#uploadStory', function () {
			epl.nav.transition(epl.views.uploadStory);
		});

		self.get('', function () {
			require(['epl', 'views'], function (epl, views) {
				epl.nav.transition(views.main);
			});
		});

	})
};

//End module
});<commit_msg>Fixing routes to uploadStory and example views.<commit_after>;
define(['lib/sammy'], function (epl) {

/******************************
 * Contains app-wide settings *
 *****************************/
return {
	siteUrl : 'http://localhost:8000',
	viewDirectory : function () { return '/static/views'; },

	//API Keys
	apiKeys : {
		'google' : {
			'maps' : 'AIzaSyA59Z_Kym_voRl--cHJzYkep3Cs-_71'
		}
	},

	//Set up application routes
	routes : Sammy(function () {

		var self = this;

		//Example route
		self.get('#example', function () {
			//The template within /static/views/example.html will be loaded into the viewport provided to the AppClass constructor
			//@see the csc.View constructor for information on transitioning between views using the parameters below
			require(['epl', 'views'], function (epl, views) {
				epl.nav.transition(views.example);
			});
		});

		//Route to Upload Story view
		this.get('#uploadStory', function () {
			require(['epl', 'views'], function (epl, views) {
				epl.nav.transition(views.uploadStory);
			});
		});

		self.get('', function () {
			require(['epl', 'views'], function (epl, views) {
				epl.nav.transition(views.main);
			});
		});

	})
};

//End module
});<|endoftext|>


<commit_before>/* Nothing should truly be "global" unless you have a good reason. */

// Global variables for "summary" incrementors
var numberFollowUp = 0;
var numberTweets = 0;

/*
  This should use JavaScript's native Date() object. Then you could
  sort on the timestamp but still display dates as you want in the table.
*/

/**
* Helper function for formatting dates from the JSON data
*
* @function formatDate
* @param {date} takes in the `created_at` key from our JSON data
* @return {displayDate} properly formatted date
*/
function formatDate(date) {
  var dateSplit = date.split(" ");
  var displayDate = dateSplit[0] + ", " + dateSplit[1] + " " + dateSplit[2];

  // Return the proper date
  return displayDate;
}

/* Or:

  $('.tweets-result').on('click', '.tweet-remove', function() {
    // all your stuff...
  });

  Then you have one event listener instead of 100. You could store the ID in a data attribute.

*/

/**
* Helper function for hiding the first element that matches
* the {selector} parameter in the {parent} parameter.
*
* @function hideClosest
* @param {parent} which parent element to traverse
* @param {selector} which selector to find in {parent}
*/
function hideClosest(parent, selector) {
  $(parent).click(function() {
    if(confirm("Are you sure you want to delete this tweet?")) {
      $(this).closest(selector).fadeOut();

      numberTweets--;

      /*
        Deleting a tweet that has follow up checked is not an edge case. It could be solved easily
        right here. This also illustrates the biggest concern with this Javascript: in order to
        solve it right here, you would have to check if the follow-up checkbox is checked. You
        should never rely on the DOM for your data.
      */

      $('.tweets-total .total').text(numberTweets);
    }
  });
}

/**
* Helper function for adding follow-ups

* @function addFollowUp
* @param {id} ID selector for each individual tweet
*/
function addFollowUp(id) {
  $(id).change(function() {
    // If "Follow Up" has been clicked
    if(this.checked) {
      numberFollowUp++;

      $('.tweets-follow-up .total').html(numberFollowUp);
    } else {
      numberFollowUp--;

      $('.tweets-follow-up .total').html(numberFollowUp);
    }
  });
}

/* If your javascript is at the bottom of your HTML, $(document).ready() is not necessary.
  A self-calling function like:

    (function($){
      ...
    })(jQuery)

  is better and faster.

  Also, all of your helper functions and variables should be INSIDE this function so the
  global scope is not polluted and the names can be minified.
*/
$(document).ready(function() {
  // Start AJAX request
  $.ajax({
    url: "https://gist.githubusercontent.com/arlodesign/7d80edb6e801e92c977a/raw/24605c9e5de897f7877b9ab72af13e5b5a2e25eb/tweets.json",
    dataType: "text",
    success: function(data) {
      // Store the JSON data
      var tweetData = $.parseJSON(data);

      /* This would be much faster:

          numberTweets = tweetData.tweets.length;

      */

      // Loop through JSON values and build the table
      $.each(tweetData.tweets, function(index, item) {
        // Count number of tweets
        numberTweets++;

        /* Templates will make your life a lot easier for this stuff.
          We use Mustache templates compiled with Hogan.
        */

        // Build and append table rows for each JSON value
        $('.tweets-result').append(
          '
' +
            '	' +
            '@' + item.screen_name + '	' +
            '' + item.text + '	' +
            '' + formatDate(item.created_at) + '	' +
            '<input id="follow-up-' + item.id + '" type="checkbox" /> Follow Up' +
                 'Remove' +
            '	' +
          '
'
        );+

        // Count tweets marked for follow-up
        addFollowUp("#follow-up-" + item.id);

        // Hide table row when "Remove" is clicked
        hideClosest("#remove-tweet-" + item.id, "tr");
      });

      /* http://www.datatables.net/manual/orthogonal-data

        Since you chose datatables for your library, you had an opportunity
        to simply pass the data directly to it. You also could have used that
        to handle the date sorting.
      */

      // Use `datatables` plugin to add sorting
      $('.table-sort').dataTable({
        // Set which columns are sortable
        "columns": [
          { "orderable": false },
          null,
          { "orderable": false },
          null,
          { "orderable": false }
        ],
        // By default, order the 3rd column ascending
        "order": [3, 'asc'],
        // Save the state of the table order in a cookie
        stateSave: true
      });

      // Display total number of tweets
      $('.tweets-total .total').text(numberTweets);
    }
  });
});
<commit_msg>One more note on tweets.js<commit_after>/* Nothing should truly be "global" unless you have a good reason. */

// Global variables for "summary" incrementors
var numberFollowUp = 0;
var numberTweets = 0;

/*
  This should use JavaScript's native Date() object. Then you could
  sort on the timestamp but still display dates as you want in the table.
*/

/**
* Helper function for formatting dates from the JSON data
*
* @function formatDate
* @param {date} takes in the `created_at` key from our JSON data
* @return {displayDate} properly formatted date
*/
function formatDate(date) {
  var dateSplit = date.split(" ");
  var displayDate = dateSplit[0] + ", " + dateSplit[1] + " " + dateSplit[2];

  // Return the proper date
  return displayDate;
}

/* Or:

  $('.tweets-result').on('click', '.tweet-remove', function() {
    // all your stuff...
  });

  Then you have one event listener instead of 100. You could store the ID in a data attribute.

*/

/**
* Helper function for hiding the first element that matches
* the {selector} parameter in the {parent} parameter.
*
* @function hideClosest
* @param {parent} which parent element to traverse
* @param {selector} which selector to find in {parent}
*/
function hideClosest(parent, selector) {
  $(parent).click(function() {
    if(confirm("Are you sure you want to delete this tweet?")) {
      $(this).closest(selector).fadeOut();

      numberTweets--;

      /*
        Deleting a tweet that has follow up checked is not an edge case. It could be solved easily
        right here by checking if the follow-up checkbox is checked.

        A number of other things need to happen here. Your datatable needs to be updated so that
        the pagination works. It's great that you added the feature, but it really needed to work.

        Things like this are an example of why relying on the DOM for your data is a no-no. How
        your data is presented in the browser should be a byproduct of the manipulation of the
        data, not the other way around. Add a few more features to this page, and you'll have
        a mess on your hands.
      */

      $('.tweets-total .total').text(numberTweets);
    }
  });
}

/**
* Helper function for adding follow-ups

* @function addFollowUp
* @param {id} ID selector for each individual tweet
*/
function addFollowUp(id) {
  $(id).change(function() {
    // If "Follow Up" has been clicked
    if(this.checked) {
      numberFollowUp++;

      $('.tweets-follow-up .total').html(numberFollowUp);
    } else {
      numberFollowUp--;

      $('.tweets-follow-up .total').html(numberFollowUp);
    }
  });
}

/* If your javascript is at the bottom of your HTML, $(document).ready() is not necessary.
  A self-calling function like:

    (function($){
      ...
    })(jQuery)

  is better and faster.

  Also, all of your helper functions and variables should be INSIDE this function so the
  global scope is not polluted and the names can be minified.
*/
$(document).ready(function() {
  // Start AJAX request
  $.ajax({
    url: "https://gist.githubusercontent.com/arlodesign/7d80edb6e801e92c977a/raw/24605c9e5de897f7877b9ab72af13e5b5a2e25eb/tweets.json",
    dataType: "text",
    success: function(data) {
      // Store the JSON data
      var tweetData = $.parseJSON(data);

      /* This would be much faster:

          numberTweets = tweetData.tweets.length;

      */

      // Loop through JSON values and build the table
      $.each(tweetData.tweets, function(index, item) {
        // Count number of tweets
        numberTweets++;

        /* Templates will make your life a lot easier for this stuff.
          We use Mustache templates compiled with Hogan.
        */

        // Build and append table rows for each JSON value
        $('.tweets-result').append(
          '
' +
            '	' +
            '@' + item.screen_name + '	' +
            '' + item.text + '	' +
            '' + formatDate(item.created_at) + '	' +
            '<input id="follow-up-' + item.id + '" type="checkbox" /> Follow Up' +
                 'Remove' +
            '	' +
          '
'
        );+

        // Count tweets marked for follow-up
        addFollowUp("#follow-up-" + item.id);

        // Hide table row when "Remove" is clicked
        hideClosest("#remove-tweet-" + item.id, "tr");
      });

      /* http://www.datatables.net/manual/orthogonal-data

        Since you chose datatables for your library, you had an opportunity
        to simply pass the data directly to it. You also could have used that
        to handle the date sorting.
      */

      // Use `datatables` plugin to add sorting
      $('.table-sort').dataTable({
        // Set which columns are sortable
        "columns": [
          { "orderable": false },
          null,
          { "orderable": false },
          null,
          { "orderable": false }
        ],
        // By default, order the 3rd column ascending
        "order": [3, 'asc'],
        // Save the state of the table order in a cookie
        stateSave: true
      });

      // Display total number of tweets
      $('.tweets-total .total').text(numberTweets);
    }
  });
});
<|endoftext|>


<commit_before>const config = require('./config.json');

module.exports = () => `
<!doctype html>

    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, maximum-scale=2.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Full on</title>
    <link href="${config.font.url}" rel="stylesheet">

`;
<commit_msg>change site title<commit_after>const config = require('./config.json');

module.exports = () => `
<!doctype html>

    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, maximum-scale=2.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Full On Youth Camp</title>
    <link href="${config.font.url}" rel="stylesheet">

`;
<|endoftext|>


<commit_before>(function() {
	// TODO replace loader with DeferJS when it supports .css loading.
	function Parameters(search) {
		var pairs = search.slice(1).split('&');
		var param, value;

		if (search === '') { return this; }

		for (var i = 0; i < pairs.length; i++) {
			param = pairs[i].split('=', 1);
			param = decodeURIComponent(param);
			value = pairs[i].split('=').slice(1).join('=');

			this[param] = decodeURIComponent(value);
		}

		return this;
	}
	Parameters.prototype['boot-uri'] = '/javascripts/vizard.boot.js';

	var protocol = location.protocol;
	var host     = location.pathname.split('/', 2)[1];
	var params   = new Parameters(location.search);

	// FIXME do not load jQuery from Google
	var src = protocol + '//ajax.googleapis.com';
	    src += '/ajax/libs/jquery/1.5.1/jquery.js';

	var script  = document.createElement('script');
	script.type = 'text/javascript';
	script.src  = src;

	document.body.appendChild(script);

	function require(src) {
		var script = $('<script type="text/javascript">').attr('src', src);
		$('body').append(script);

		return script;
	}

	function boot() {
		if (!window.jQuery) { return; }
		clearInterval(interval);

		require('/js/jquery.simple-toolbar.js');
		require('/js/xhtml-0.3.min.js');
		require('/js/jquery.vizard-0.4.core.js').ready(function() {
			var path = '/' + location.pathname.split('/').slice(2).join('/');

			Vizard.location = {
				host: host,
				hostname: host.split(':', 1)[0],
				href: protocol + '//' + host + path,
				pathname: path,
				port: host.split(':', 2)[1],
				protocol: protocol,
				__proto__: location.__proto__
			};
			Vizard.params = params;

			require(protocol + '//' + host + params['boot-uri']);
		});
	};

	// set an interval because IEs readystatechange gets overwritten by jQuery
	var interval = setInterval(boot, 25);

	// export load function
	this.require = require;

})();
<commit_msg>Enable loader to load stylesheets, too.<commit_after>(function() {
	// TODO replace loader with DeferJS when it supports .css loading.
	function Parameters(search) {
		var pairs = search.slice(1).split('&');
		var param, value;

		if (search === '') { return this; }

		for (var i = 0; i < pairs.length; i++) {
			param = pairs[i].split('=', 1);
			param = decodeURIComponent(param);
			value = pairs[i].split('=').slice(1).join('=');

			this[param] = decodeURIComponent(value);
		}

		return this;
	}
	Parameters.prototype['boot-uri'] = '/javascripts/vizard.boot.js';

	var protocol = location.protocol;
	var host     = location.pathname.split('/', 2)[1];
	var params   = new Parameters(location.search);

	// FIXME do not load jQuery from Google
	var src = protocol + '//ajax.googleapis.com';
	    src += '/ajax/libs/jquery/1.5.1/jquery.js';

	var script  = document.createElement('script');
	script.type = 'text/javascript';
	script.src  = src;

	document.body.appendChild(script);

	var jsExtname = /\.js$/, cssExtname = /\/.css$/;
	function require(src) {
		var req;
		if (jsExtname.test(src)) {
			req = $('<script type="text/javascript">').attr('src', src);
			$('body').append(req);
		} else if (cssExtname.test(src)) {
			req = $('<link rel="stylesheet" type="text/css" charset="utf-8">').attr('href', src);
			$('head').append(req);
		}

		return req;
	}

	function boot() {
		if (!window.jQuery) { return; }
		clearInterval(interval);

		require('/js/jquery.simple-toolbar.js');
		require('/js/xhtml-0.3.min.js');
		require('/js/jquery.vizard-0.4.core.js').ready(function() {
			var path = '/' + location.pathname.split('/').slice(2).join('/');

			Vizard.location = {
				host: host,
				hostname: host.split(':', 1)[0],
				href: protocol + '//' + host + path,
				pathname: path,
				port: host.split(':', 2)[1],
				protocol: protocol,
				__proto__: location.__proto__
			};
			Vizard.params = params;

			require(protocol + '//' + host + params['boot-uri']);
		});
	};

	// set an interval because IEs readystatechange gets overwritten by jQuery
	var interval = setInterval(boot, 25);

	// export load function
	this.require = require;

})();
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const resolveFrom = require('resolve-from');

module.exports = {

	'settings': {
		react: {
			version: '16'
		}
	},

	'parser': resolveFrom(path.dirname(require.resolve('eslint-plugin-vue')), 'vue-eslint-parser'),

	'extends': require.resolve('./lib/jsx'),

	'parserOptions': {
		sourceType: 'module'
	},

	'plugins': ['vue'],

	'rules': {

		// Other
		'no-param-reassign': [
			1,
			{
				props: true,
				ignorePropertyModificationsFor: ['state']
			}
		],

		// Base rules
		'vue/comment-directive': 2,
		'vue/jsx-uses-vars': 2,

		// Essential
		'vue/no-async-in-computed-properties': 2,
		'vue/no-dupe-keys': 2,
		'vue/no-duplicate-attributes': [ 2, { allowCoexistClass: true, allowCoexistStyle: true }],
		'vue/no-parsing-error': 2,
		'vue/no-reserved-keys': 2,
		'vue/no-shared-component-data': 2,
		'vue/no-side-effects-in-computed-properties': 2,
		'vue/no-template-key': 2,
		'vue/no-textarea-mustache': 2,
		'vue/no-unused-vars': 1,
		'vue/require-component-is': 2,
		'vue/require-render-return': 2,
		'vue/require-v-for-key': 2,
		'vue/require-valid-default-prop': 2,
		'vue/return-in-computed-property': 2,
		'vue/valid-template-root': 2,
		'vue/valid-v-bind': 2,
		'vue/valid-v-cloak': 2,
		'vue/valid-v-else-if': 2,
		'vue/valid-v-else': 2,
		'vue/valid-v-for': 2,
		'vue/valid-v-html': 2,
		'vue/valid-v-if': 2,
		'vue/valid-v-model': 2,
		'vue/valid-v-on': 2,
		'vue/valid-v-once': 2,
		'vue/valid-v-pre': 2,
		'vue/valid-v-show': 2,
		'vue/valid-v-text': 2,
		'vue/no-unused-components': 1,
		'vue/require-prop-type-constructor': 2,

		// Strongly recommended
		'vue/attribute-hyphenation': [ 2, 'never' ],
		'vue/html-end-tags': 2,
		'vue/html-indent': [ 2, 'tab' ],
		'vue/html-self-closing': [
			2,
			{
				html: {
					'void': 'always',
					'normal': 'never',
					'component': 'always'
				},
				svg: 'always',
				math: 'always'
			}
		],
		'vue/max-attributes-per-line': [
			2,
			{
				singleline: 3,
				multiline: {
					max: 1,
					allowFirstLine: false
				}
			}
		],
		'vue/mustache-interpolation-spacing': [ 2, 'always' ],
		'vue/name-property-casing': [ 2, 'PascalCase' ],
		'vue/no-multi-spaces': 2,
		'vue/require-default-prop': 1,
		'vue/require-prop-types': 1,
		'vue/v-bind-style': [ 2, 'shorthand' ],
		'vue/v-on-style': [ 2, 'shorthand' ],
		'vue/no-template-shadow': 2,

		// Recommended
		'vue/attributes-order': 2,
		'vue/html-quotes': [ 2, 'double' ],
		'vue/no-confusing-v-for-v-if': 2,
		'vue/order-in-components': 2,
		'vue/this-in-template': [ 1, 'never' ], // TODO: review later
		'vue/no-use-v-if-with-v-for': 2,
		'vue/no-v-html': 1,

		// Uncategorized
		'vue/html-closing-bracket-newline': [
			2,
			{
				singleline: 'never',
				multiline: 'always'
			}
		],
		'vue/html-closing-bracket-spacing': [
			2,
			{
				startTag: 'never',
				endTag: 'never',
				selfClosingTag: 'always'
			}
		],
		'vue/prop-name-casing': [ 2, 'camelCase' ],
		'vue/script-indent': [
			2,
			'tab',
			{
				baseIndent: 1,
				switchCase: 1
			}
		],
		'vue/component-name-in-template-casing': [ 2, 'PascalCase' ],
		'vue/multiline-html-element-content-newline': 1,
		'vue/singleline-html-element-content-newline': 0,
		'vue/no-spaces-around-equal-signs-in-attribute': 2

	},

	'overrides': [
		{
			files: ['*.vue'],
			rules: {
				indent: 0
			}
		},
		{
			files: ['!*.vue'],
			rules: {
				'vue/script-indent': 0
			}
		}
	]

};
<commit_msg>Adjust Vue template casing for component names<commit_after>'use strict';

const path = require('path');
const resolveFrom = require('resolve-from');

module.exports = {

	'settings': {
		react: {
			version: '16'
		}
	},

	'parser': resolveFrom(path.dirname(require.resolve('eslint-plugin-vue')), 'vue-eslint-parser'),

	'extends': require.resolve('./lib/jsx'),

	'parserOptions': {
		sourceType: 'module'
	},

	'plugins': ['vue'],

	'rules': {

		// Other
		'no-param-reassign': [
			1,
			{
				props: true,
				ignorePropertyModificationsFor: ['state']
			}
		],

		// Base rules
		'vue/comment-directive': 2,
		'vue/jsx-uses-vars': 2,

		// Essential
		'vue/no-async-in-computed-properties': 2,
		'vue/no-dupe-keys': 2,
		'vue/no-duplicate-attributes': [ 2, { allowCoexistClass: true, allowCoexistStyle: true }],
		'vue/no-parsing-error': 2,
		'vue/no-reserved-keys': 2,
		'vue/no-shared-component-data': 2,
		'vue/no-side-effects-in-computed-properties': 2,
		'vue/no-template-key': 2,
		'vue/no-textarea-mustache': 2,
		'vue/no-unused-vars': 1,
		'vue/require-component-is': 2,
		'vue/require-render-return': 2,
		'vue/require-v-for-key': 2,
		'vue/require-valid-default-prop': 2,
		'vue/return-in-computed-property': 2,
		'vue/valid-template-root': 2,
		'vue/valid-v-bind': 2,
		'vue/valid-v-cloak': 2,
		'vue/valid-v-else-if': 2,
		'vue/valid-v-else': 2,
		'vue/valid-v-for': 2,
		'vue/valid-v-html': 2,
		'vue/valid-v-if': 2,
		'vue/valid-v-model': 2,
		'vue/valid-v-on': 2,
		'vue/valid-v-once': 2,
		'vue/valid-v-pre': 2,
		'vue/valid-v-show': 2,
		'vue/valid-v-text': 2,
		'vue/no-unused-components': 1,
		'vue/require-prop-type-constructor': 2,

		// Strongly recommended
		'vue/attribute-hyphenation': [ 2, 'never' ],
		'vue/html-end-tags': 2,
		'vue/html-indent': [ 2, 'tab' ],
		'vue/html-self-closing': [
			2,
			{
				html: {
					'void': 'always',
					'normal': 'never',
					'component': 'always'
				},
				svg: 'always',
				math: 'always'
			}
		],
		'vue/max-attributes-per-line': [
			2,
			{
				singleline: 3,
				multiline: {
					max: 1,
					allowFirstLine: false
				}
			}
		],
		'vue/mustache-interpolation-spacing': [ 2, 'always' ],
		'vue/name-property-casing': [ 2, 'PascalCase' ],
		'vue/no-multi-spaces': 2,
		'vue/require-default-prop': 1,
		'vue/require-prop-types': 1,
		'vue/v-bind-style': [ 2, 'shorthand' ],
		'vue/v-on-style': [ 2, 'shorthand' ],
		'vue/no-template-shadow': 2,

		// Recommended
		'vue/attributes-order': 2,
		'vue/html-quotes': [ 2, 'double' ],
		'vue/no-confusing-v-for-v-if': 2,
		'vue/order-in-components': 2,
		'vue/this-in-template': [ 1, 'never' ], // TODO: review later
		'vue/no-use-v-if-with-v-for': 2,
		'vue/no-v-html': 1,

		// Uncategorized
		'vue/html-closing-bracket-newline': [
			2,
			{
				singleline: 'never',
				multiline: 'always'
			}
		],
		'vue/html-closing-bracket-spacing': [
			2,
			{
				startTag: 'never',
				endTag: 'never',
				selfClosingTag: 'always'
			}
		],
		'vue/prop-name-casing': [ 2, 'camelCase' ],
		'vue/script-indent': [
			2,
			'tab',
			{
				baseIndent: 1,
				switchCase: 1
			}
		],
		'vue/component-name-in-template-casing': [
			2,
			'PascalCase',
			{
				ignores: [
					'svg',
					'rect'
				]
			}
		],
		'vue/multiline-html-element-content-newline': 1,
		'vue/singleline-html-element-content-newline': 0,
		'vue/no-spaces-around-equal-signs-in-attribute': 2

	},

	'overrides': [
		{
			files: ['*.vue'],
			rules: {
				indent: 0
			}
		},
		{
			files: ['!*.vue'],
			rules: {
				'vue/script-indent': 0
			}
		}
	]

};
<|endoftext|>


<commit_before>/**
 * Package.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/documentation/concepts/models-and-orm/models
 */

var Promise = require('bluebird');

module.exports = {

  attributes: {
    name: {
      type: Sequelize.STRING,
      unique: true,
      primaryKey: true,
      allowNull: false
    }

  },
  associations: function() {
    Package.hasMany(PackageVersion,
      {
        as: 'versions',
        foreignKey: {
          name: 'package_name',
          as: 'versions'
        }
      }
    );
    Package.belongsTo(PackageVersion, {
      as: 'latest_version',
      foreignKey: {
        name:'latest_version_id',
        as: 'latest_version'
      },
      constraints: false }
    );

    Package.belongsTo(Repository, {
      as: 'repository',
      foreignKey: {
        name:'type_id',
        allowNull: true,
        as: 'repository'
      }
    });

    Package.belongsToMany(TaskView, {
      as: 'inViews',
      foreignKey: 'package_name',
      through: 'TaskViewPackages',
      timestamps: false
    });

    Package.belongsToMany(PackageVersion, { as: 'reverse_dependencies', foreignKey: 'dependency_name', through: Dependency, constraints: false});

    Package.hasOne(DownloadStatistic,
      {
        as: 'last_month_stats',
        foreignKey: {
          name: 'package_name',
          as: 'last_month_stats'
        }
      }
    );

    Package.hasMany(Star,
      {
        as: 'stars',
        foreignKey: 'package_name'
      }
    );
  },

  options: {
    getterMethods: {
      api_uri: function()  {
        return '/api/packages/:name'
          .replace(':name', encodeURIComponent(this.getDataValue('name')));
      },
      uri: function()  {
        return '/packages/:name'
          .replace(':name', encodeURIComponent(this.getDataValue('name')));
      }
    },
    underscored: true,

    classMethods: {
      getLatestVersionNumber :function(package_name){
        return Package.findOne({
          include:[{
            model:PackageVersion,
            as:'latest_version',
            attributes:['version'],
            required:true
          }],
          where:{
            name:package_name
          }
        });
      },

      getAllNamesOfType:function(type){
        return Package.findAll({
          where:{type_id:type}
        }).then(function(packages){
          return _.map(packages,function(package){
            return package.dataValues.name;
          })
        })
      },

      getPackagePercentile: function(name) {
        var lastMonthPercentiles = ElasticSearchService.cachedLastMonthPercentiles();

        var lastMonthDownload = sails.controllers.packageversion._getDownloadStatistics(undefined, name);

        return Promise.join(lastMonthPercentiles, lastMonthDownload, function(percentilesResponse, downloads) {
          var total = downloads.total;

          var percentiles = _.omit(percentilesResponse, 'fromCache');
          var percentile = _.findLastKey(percentiles, function(p) {
            return total >= p;
          });

          return {total: total, percentile: Math.round(percentile * 100) / 100 };
        });
      }
    }
  }
};

<commit_msg>fix percentile for collaborators<commit_after>/**
 * Package.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/documentation/concepts/models-and-orm/models
 */

var Promise = require('bluebird');

module.exports = {

  attributes: {
    name: {
      type: Sequelize.STRING,
      unique: true,
      primaryKey: true,
      allowNull: false
    }

  },
  associations: function() {
    Package.hasMany(PackageVersion,
      {
        as: 'versions',
        foreignKey: {
          name: 'package_name',
          as: 'versions'
        }
      }
    );
    Package.belongsTo(PackageVersion, {
      as: 'latest_version',
      foreignKey: {
        name:'latest_version_id',
        as: 'latest_version'
      },
      constraints: false }
    );

    Package.belongsTo(Repository, {
      as: 'repository',
      foreignKey: {
        name:'type_id',
        allowNull: true,
        as: 'repository'
      }
    });

    Package.belongsToMany(TaskView, {
      as: 'inViews',
      foreignKey: 'package_name',
      through: 'TaskViewPackages',
      timestamps: false
    });

    Package.belongsToMany(PackageVersion, { as: 'reverse_dependencies', foreignKey: 'dependency_name', through: Dependency, constraints: false});

    Package.hasOne(DownloadStatistic,
      {
        as: 'last_month_stats',
        foreignKey: {
          name: 'package_name',
          as: 'last_month_stats'
        }
      }
    );

    Package.hasMany(Star,
      {
        as: 'stars',
        foreignKey: 'package_name'
      }
    );
  },

  options: {
    getterMethods: {
      api_uri: function()  {
        return '/api/packages/:name'
          .replace(':name', encodeURIComponent(this.getDataValue('name')));
      },
      uri: function()  {
        return '/packages/:name'
          .replace(':name', encodeURIComponent(this.getDataValue('name')));
      }
    },
    underscored: true,

    classMethods: {
      getLatestVersionNumber :function(package_name){
        return Package.findOne({
          include:[{
            model:PackageVersion,
            as:'latest_version',
            attributes:['version'],
            required:true
          }],
          where:{
            name:package_name
          }
        });
      },

      getAllNamesOfType:function(type){
        return Package.findAll({
          where:{type_id:type}
        }).then(function(packages){
          return _.map(packages,function(package){
            return package.dataValues.name;
          })
        })
      },

      getPackagePercentile: function(name) {
        var lastMonthPercentiles = Percentile.findAll();

        var lastMonthDownload = sails.controllers.packageversion._getDownloadStatistics(undefined, name);

        return Promise.join(lastMonthPercentiles, lastMonthDownload, function(percentilesResponse, downloads) {
          var total = downloads.total;

          var percentiles = _.mapValues(_.keyBy(percentilesResponse,"percentile"),function(a){return a.value;});
          var percentile = _.findLastKey(percentiles, function(p) {
            return total >= p;
          });

          return {total: total, percentile: Math.round(percentile * 100) / 100 };
        });
      }
    }
  }
};

<|endoftext|>