<commit_before>
Ink.requireModules(['Ink.UI.Carousel_1', 'Ink.UI.Pagination_1', 'Ink.Dom.Element_1', 'Ink.Dom.Event_1', 'Ink.Dom.Selector_1'], function (Carousel, Pagination, InkElement, InkEvent, Selector) {
    'use strict';

    function makeContainer(options) {
        options = options || {}
        var cont = InkElement.create('div', {
            className: 'ink-carousel',
            insertBottom: document.body
        });

        cont.style.width = '100px';

        var stage = cont.appendChild(InkElement.create('ul', {
            'class': 'unstyled stage'
        }));

        if (!options.animation) {
            stage.style.transitionDuration = '0s';
        }

        for (var i = 0; i < 5; i++) {
            var slide = stage.appendChild(InkElement.create('li', {
                'class': options.uneven ? 'slide all-60' : 'slide all-100',
                setTextContent: '' + i
            }));

            slide.style.minHeight = '50px';
        }

        return cont;
    }

    function testCarousel(name, testBack, options) {
        options = options || {};
        test(name, function ()  {
            var container = makeContainer(options);
            var stage = Ink.s('.stage', container);
            var slides = Ink.ss('.stage .slide', container);
            if (pag) {
                options.pagination = pag;
            }
            var carouselComponent = new Carousel(container, options);
            testBack(carouselComponent, container, stage, slides);
        });
    }

    test('(regression) creating a carousel doesn\'t call the onChange callback', function() {
        var onChange = sinon.spy()
        new Carousel(makeContainer(), { onChange: onChange });
        ok(onChange.notCalled, 'onChange not called')

        var onChange = sinon.spy()
        new Carousel(makeContainer(), { initialPage: 2, onChange: onChange });
        ok(onChange.notCalled, 'onChange not called even with an initialPage set')
    })

    module('_setPage()');

    testCarousel('Percentages are used to pull the slides left.', function (carousel, _, stage) {
        carousel._setPage(1);
        equal(stage.style.left, '-100%');
    });

    testCarousel('... even when the slide widths are uneven', function (carousel, _, stage) {
        carousel._setPage(1);
        equal(stage.style.left, '-60%');
    }, { uneven: true });

    var pagElm;
    var pag;
    module('Pagination integration', {
        setup: function () {
            pagElm = InkElement.create('div', { insertBottom: document.body });
            pag = new Pagination(pagElm, { size: 10 });
        },
        teardown: function () {
            pagElm = pag = null;
        }
    });

    testCarousel('Controlling the paginator sets the page on the Carousel', sinon.test(function (carousel) {
        this.stub(carousel, '_setPage');
        pag.setCurrent(2);
        equal(carousel._setPage.lastCall.args[0], 2);
    }));

    if ('ontouchstart' in document) {
        module('Touch');

        testCarousel('You can swipe the carousel sideways to trigger a page change', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 101, 10);
            utils.dispatchTouchEvent(stage, 'move', 1, 10);
            ok(carousel.setPage.notCalled, 'setPage() not called because finger is not up');
            utils.dispatchTouchEvent(stage, 'end', 1, 10);
            ok(carousel.setPage.calledOnce);
            equal(carousel.setPage.lastCall.args[0], 1);
        });

        testCarousel('You can swipe the carousel sideways to trigger a page change (part 2: swipe left)', function (carousel, _, stage) {
            carousel.setPage(1);
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 1, 10);
            utils.dispatchTouchEvent(stage, 'move', 101, 10);
            utils.dispatchTouchEvent(stage, 'end', 101, 10);
            ok(carousel.setPage.calledOnce);
            equal(carousel.setPage.lastCall.args[0], 0);
        });

        testCarousel('Smaller swipes don\'t count.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 100, 10);
            utils.dispatchTouchEvent(stage, 'move', 98, 10);
            utils.dispatchTouchEvent(stage, 'end', 98, 10);
            ok(carousel.setPage.calledWith(0), 'Page hasn\'t changed');
        })

        testCarousel('But if you swipe about 1/3 of the way the page changes.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 100, 10);
            utils.dispatchTouchEvent(stage, 'move', 66, 10);
            utils.dispatchTouchEvent(stage, 'end', 66, 10);
            ok(carousel.setPage.calledWith(1), 'Page changed');
        })

        testCarousel('Regression: Page changes even if you swipe off the left of the carousel.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 1, 10);
            utils.dispatchTouchEvent(stage, 'move', 99, 10);
            utils.dispatchTouchEvent(stage, 'end', 99, 10);
            ok(carousel.setPage.calledOnce, 'Page changed');
            ok(carousel.setPage.calledWith(0), 'Page changed');
        })

        testCarousel('Regression: Page changes even if you swipe off the right of the carousel.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 99, 10);
            utils.dispatchTouchEvent(stage, 'move', 1, 10);
            utils.dispatchTouchEvent(stage, 'end', 1, 10);
            ok(carousel.setPage.calledOnce, 'Page changed');
            ok(carousel.setPage.calledWith(4), 'Page changed');
        }, { initialPage: 4 })
    }

    function testCarouselRefit(name, cb) {
        testCarousel(name, function (carousel, container, stage, slides) {
            var wrapper = InkElement.create('div', { style: 'width: 100px' });
            container.parentNode.appendChild(wrapper);
            wrapper.appendChild(container);
            cb(carousel, wrapper, container, stage, slides)
        })
    }
    module('refit()');

    testCarouselRefit('refit() won\'t call onChange unnecessarily', function (carousel, wrapper) {
        var onChange = sinon.stub()
        carousel.setOption('onChange', onChange);
        carousel.refit();
        ok(onChange.notCalled)
    });
});
<commit_msg>Oops, page change test should stub _changePage, not changePage<commit_after>
Ink.requireModules(['Ink.UI.Carousel_1', 'Ink.UI.Pagination_1', 'Ink.Dom.Element_1', 'Ink.Dom.Event_1', 'Ink.Dom.Selector_1'], function (Carousel, Pagination, InkElement, InkEvent, Selector) {
    'use strict';

    function makeContainer(options) {
        options = options || {}
        var cont = InkElement.create('div', {
            className: 'ink-carousel',
            insertBottom: document.body
        });

        cont.style.width = '100px';

        var stage = cont.appendChild(InkElement.create('ul', {
            'class': 'unstyled stage'
        }));

        if (!options.animation) {
            stage.style.transitionDuration = '0s';
        }

        for (var i = 0; i < 5; i++) {
            var slide = stage.appendChild(InkElement.create('li', {
                'class': options.uneven ? 'slide all-60' : 'slide all-100',
                setTextContent: '' + i
            }));

            slide.style.minHeight = '50px';
        }

        return cont;
    }

    function testCarousel(name, testBack, options) {
        options = options || {};
        test(name, function ()  {
            var container = makeContainer(options);
            var stage = Ink.s('.stage', container);
            var slides = Ink.ss('.stage .slide', container);
            if (pag) {
                options.pagination = pag;
            }
            var carouselComponent = new Carousel(container, options);
            testBack(carouselComponent, container, stage, slides);
        });
    }

    test('(regression) creating a carousel doesn\'t call the onChange callback', function() {
        var onChange = sinon.spy()
        new Carousel(makeContainer(), { onChange: onChange });
        ok(onChange.notCalled, 'onChange not called')

        var onChange = sinon.spy()
        new Carousel(makeContainer(), { initialPage: 2, onChange: onChange });
        ok(onChange.notCalled, 'onChange not called even with an initialPage set')
    })

    module('_setPage()');

    testCarousel('Percentages are used to pull the slides left.', function (carousel, _, stage) {
        carousel._setPage(1);
        equal(stage.style.left, '-100%');
    });

    testCarousel('... even when the slide widths are uneven', function (carousel, _, stage) {
        carousel._setPage(1);
        equal(stage.style.left, '-60%');
    }, { uneven: true });

    var pagElm;
    var pag;
    module('Pagination integration', {
        setup: function () {
            pagElm = InkElement.create('div', { insertBottom: document.body });
            pag = new Pagination(pagElm, { size: 10 });
        },
        teardown: function () {
            pagElm = pag = null;
        }
    });

    testCarousel('Controlling the paginator sets the page on the Carousel', sinon.test(function (carousel) {
        this.stub(carousel, '_setPage');
        pag.setCurrent(2);
        equal(carousel._setPage.lastCall.args[0], 2);
    }));

    if ('ontouchstart' in document) {
        module('Touch');

        testCarousel('You can swipe the carousel sideways to trigger a page change', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 101, 10);
            utils.dispatchTouchEvent(stage, 'move', 1, 10);
            ok(carousel.setPage.notCalled, 'setPage() not called because finger is not up');
            utils.dispatchTouchEvent(stage, 'end', 1, 10);
            ok(carousel.setPage.calledOnce);
            equal(carousel.setPage.lastCall.args[0], 1);
        });

        testCarousel('You can swipe the carousel sideways to trigger a page change (part 2: swipe left)', function (carousel, _, stage) {
            carousel.setPage(1);
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 1, 10);
            utils.dispatchTouchEvent(stage, 'move', 101, 10);
            utils.dispatchTouchEvent(stage, 'end', 101, 10);
            ok(carousel.setPage.calledOnce);
            equal(carousel.setPage.lastCall.args[0], 0);
        });

        testCarousel('Smaller swipes don\'t count.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 100, 10);
            utils.dispatchTouchEvent(stage, 'move', 98, 10);
            utils.dispatchTouchEvent(stage, 'end', 98, 10);
            ok(carousel.setPage.calledWith(0), 'Page hasn\'t changed');
        })

        testCarousel('But if you swipe about 1/3 of the way the page changes.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 100, 10);
            utils.dispatchTouchEvent(stage, 'move', 66, 10);
            utils.dispatchTouchEvent(stage, 'end', 66, 10);
            ok(carousel.setPage.calledWith(1), 'Page changed');
        })

        testCarousel('Regression: Page changes even if you swipe off the left of the carousel.', function (carousel, _, stage) {
            sinon.stub(carousel, '_setPage');
            utils.dispatchTouchEvent(stage, 'start', 1, 10);
            utils.dispatchTouchEvent(stage, 'move', 99, 10);
            utils.dispatchTouchEvent(stage, 'end', 99, 10);
            ok(carousel._setPage.calledOnce, 'Page changed');
            ok(carousel._setPage.calledWith(0), 'Page changed');
        })

        testCarousel('Regression: Page changes even if you swipe off the right of the carousel.', function (carousel, _, stage) {
            sinon.stub(carousel, '_setPage');
            utils.dispatchTouchEvent(stage, 'start', 99, 10);
            utils.dispatchTouchEvent(stage, 'move', 1, 10);
            utils.dispatchTouchEvent(stage, 'end', 1, 10);
            ok(carousel._setPage.calledOnce, 'Page changed');
            ok(carousel._setPage.calledWith(4), 'Page changed');
        }, { initialPage: 4 })
    }

    function testCarouselRefit(name, cb) {
        testCarousel(name, function (carousel, container, stage, slides) {
            var wrapper = InkElement.create('div', { style: 'width: 100px' });
            container.parentNode.appendChild(wrapper);
            wrapper.appendChild(container);
            cb(carousel, wrapper, container, stage, slides)
        })
    }
    module('refit()');

    testCarouselRefit('refit() won\'t call onChange unnecessarily', function (carousel, wrapper) {
        var onChange = sinon.stub()
        carousel.setOption('onChange', onChange);
        carousel.refit();
        ok(onChange.notCalled)
    });
});
<|endoftext|>


<commit_before>var mongoose = require('mongoose'),
    shortid = require('shortid');

// TODO
// Enkeltbrukere opprettes med organization.
var UserSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    username: {type: String, lowercase: true, trim: true, required: true, unique: true},
    name: {type: String, required: true},
    email: {type: String},
    password: {type: String},
    is_active: {type: Boolean, 'default': true},
    is_admin: {type: Boolean, 'default': false},
    created: {type: Date, required: true, 'default': Date.now},
    google_id: {type: String}
});

var OrganizationSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    admins: [{type: String, ref: 'User'}],
    name: {type: String, trim: true, required: true},
    users: [{type: String, ref: 'User'}],
    created: {type: Date, required: true, 'default': Date.now}
});

var ProjectSchema = new mongoose.Schema({
    name: {type: String, trim: true, required: true},
    client: {type: String, trim: true, ref: 'Client'},
    user: {type: String, ref: 'User', required: true}, // user who created project
    organization: {type: String, ref: 'Organization', required: true},
    users: [{type: String, ref: 'User'}], // deprecated
    active: {type: Date},
    created: {type: Date, required: true, 'default': Date.now},
    modified: {type: Date, required: true, 'default': Date.now}
});

var NewProjectSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    name: {type: String, trim: true, required: true},
    client: {type: String, trim: true, ref: 'Client'},
    user: {type: String, ref: 'User', required: true}, // user who created project
    organization: {type: String, ref: 'Organization', required: true},
    users: [{type: String, ref: 'User'}], // deprecated
    active: {type: Date},
    created: {type: Date, required: true, 'default': Date.now},
    modified: {type: Date, required: true, 'default': Date.now}
});

var NewHoursSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    user: {type: String, ref: 'User', required: true},
    project: {type: String, ref: 'Project', required: true},
    date: {type: Date, required: true},
    start: {type: Date}, // use this for date
    end: {type: Date},
    duration: {type: Number, required: true},
    comment: {type: String, trim: true},
    created: {type: Date, required: true, 'default': Date.now},
    invoice: {type: String, ref: 'Invoice'}
});

var HoursSchema = new mongoose.Schema({
    //_id: {type: String, required: true, unique: true, 'default': shortid.generate},
    user: {type: String, ref: 'User', required: true},
    project: {type: String, ref: 'Project', required: true},
    date: {type: Date, required: true},
    start: {type: Date}, // use this for date
    end: {type: Date},
    duration: {type: Number, required: true},
    comment: {type: String, trim: true},
    created: {type: Date, required: true, 'default': Date.now},
    invoice: {type: String, ref: 'Invoice'}
});

var ClientSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    user: {type: String, ref: 'User', required: true}, // user who created client
    organization: {type: String, ref: 'Organization'},
    name: {type: String, required: true}
});

var InvoiceSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    user: {type: String, ref: 'User', required: true}, // who made it
    created: {type: Date, required: true, 'default': Date.now},
    start: {type: Date, required: true},
    end: {type: Date, required: true},
    sum: {type: Number, required: true},
    client: {type: String, ref: 'Client'}
});

module.exports = {
    User: mongoose.model('User', UserSchema),
    Project: mongoose.model('Project', ProjectSchema),
    NewProject: mongoose.model('NewProject', NewProjectSchema),
    Hours: mongoose.model('Hours', HoursSchema),
    NewHours: mongoose.model('NewHours', NewHoursSchema),
    Organization: mongoose.model('Organization', OrganizationSchema),
    Client: mongoose.model('Client', ClientSchema),
    Invoice: mongoose.model('Invoice', InvoiceSchema)
};
<commit_msg>End migration<commit_after>var mongoose = require('mongoose'),
    shortid = require('shortid');

// TODO
// Enkeltbrukere opprettes med organization.
var UserSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    username: {type: String, lowercase: true, trim: true, required: true, unique: true},
    name: {type: String, required: true},
    email: {type: String},
    password: {type: String},
    is_active: {type: Boolean, 'default': true},
    is_admin: {type: Boolean, 'default': false},
    created: {type: Date, required: true, 'default': Date.now},
    google_id: {type: String}
});

var OrganizationSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    admins: [{type: String, ref: 'User'}],
    name: {type: String, trim: true, required: true},
    users: [{type: String, ref: 'User'}],
    created: {type: Date, required: true, 'default': Date.now}
});

var ProjectSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    name: {type: String, trim: true, required: true},
    client: {type: String, trim: true, ref: 'Client'},
    user: {type: String, ref: 'User', required: true}, // user who created project
    organization: {type: String, ref: 'Organization', required: true},
    users: [{type: String, ref: 'User'}], // deprecated
    active: {type: Date},
    created: {type: Date, required: true, 'default': Date.now},
    modified: {type: Date, required: true, 'default': Date.now}
});

var HoursSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    user: {type: String, ref: 'User', required: true},
    project: {type: String, ref: 'Project', required: true},
    date: {type: Date, required: true},
    start: {type: Date}, // use this for date
    end: {type: Date},
    duration: {type: Number, required: true},
    comment: {type: String, trim: true},
    created: {type: Date, required: true, 'default': Date.now},
    invoice: {type: String, ref: 'Invoice'}
});

var ClientSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    user: {type: String, ref: 'User', required: true}, // user who created client
    organization: {type: String, ref: 'Organization'},
    name: {type: String, required: true}
});

var InvoiceSchema = new mongoose.Schema({
    _id: {type: String, required: true, unique: true, 'default': shortid.generate},
    user: {type: String, ref: 'User', required: true}, // who made it
    created: {type: Date, required: true, 'default': Date.now},
    start: {type: Date, required: true},
    end: {type: Date, required: true},
    sum: {type: Number, required: true},
    client: {type: String, ref: 'Client'}
});

module.exports = {
    User: mongoose.model('User', UserSchema),
    Project: mongoose.model('Project', ProjectSchema),
    NewProject: mongoose.model('NewProject', NewProjectSchema),
    Hours: mongoose.model('Hours', HoursSchema),
    NewHours: mongoose.model('NewHours', NewHoursSchema),
    Organization: mongoose.model('Organization', OrganizationSchema),
    Client: mongoose.model('Client', ClientSchema),
    Invoice: mongoose.model('Invoice', InvoiceSchema)
};
<|endoftext|>


<commit_before>/**
	A control that displays a scrolling list of rows. It is suitable for displaying very large
	lists. List is optimized such that only a small portion of the list is rendered
	at a given time. A flyweight pattern is employed such that the controls placed inside the list
	are created once but are rendered for each list item.  For this reason, it's best to use
	only simple controls in an enyo.List like enyo.Control and enyo.Image.

	## Basic Use

	A List's components block contains the controls to be used for a single row.
	This set of controls will be rendered for each row.

	The onSetupItem event allows for customization of row rendering. Here's a simple example:

		components: [
			{kind: "List", fit: true, count: 1000, onSetupItem: "setupItem", components: [
				{classes: "item", ontap: "itemTap", components: [
					{name: "name"},
					{name: "index", style: "float: right;"}
				]}
			]}
		],
		setupItem: function(inSender, inEvent) {
			// given some available data.
			var data = this.data[inEvent.index];
			// setup the controls for this item.
			this.$.name.setContent(data.name);
			this.$.index.setContent(inEvent.index);
		}

	Events fired from within list rows contain the _index_ property which can used to identify the row
	in which the event is originated from.

		itemTap: function(inSender, inEvent) {
			alert("You tapped on row: " + inEvent.index);
		}

	## Modifying List Rows

	Controls inside a list are not interactive. This means that outside the onSetupItem event,
	calling methods that would otherwise cause rendering to occur will not do so (e.g. setContent).
	A row can be forced to render by calling the renderRow(inRow) method. In addition, a row can be
	temporarily made interactive by calling the prepareRow(inRow) method. When interaction is complete, the
	lockRow() method should be called.
*/
enyo.kind({
	name: "enyo.List",
	kind: "Scroller",
	classes: "enyo-list",
	published: {
		//* The number of rows contained in the list. Note, as the amount of list data changes
		//* setRows can be called to adjust the number of rows. To re-render the list at the
		//* current position when count has changed, call the refresh() method.
		count: 0,
		//* The number of rows to be shown on a given list page segment.
		//* It is not common to need to adjust this.
		rowsPerPage: 50,
		//* Render the list such that row 0 is at the bottom of the viewport
		//* and the beginning position of the list is scrolled to the bottom.
		bottomUp: false,
		//* If true, allow multiple selections
		multiSelect: false,
		//* If true, the selected item will toggle
		toggleSelected: false,
		//* If true, the list would assume all rows have the same height for optimization
		fixedHeight: false
	},
	events: {
		//* Fired once per row at render-time, with event object: {index: <index of row>}
		onSetupItem: ""
	},
	handlers: {
		onAnimateFinish: "animateFinish"
	},
	//* @protected
	rowHeight: 0,
	listTools: [
		{name: "port", classes: "enyo-list-port enyo-border-box", components: [
			{name: "generator", kind: "FlyweightRepeater", canGenerate: false, components: [
				{tag: null, name: "client"}
			]},
			{name: "page0", allowHtml: true, classes: "enyo-list-page"},
			{name: "page1", allowHtml: true, classes: "enyo-list-page"}
		]}
	],
	create: function() {
		this.pageHeights = [];
		this.inherited(arguments);
		this.getStrategy().translateOptimized = true;
		this.bottomUpChanged();
		this.multiSelectChanged();
		this.toggleSelectedChanged();
	},
	createStrategy: function() {
		this.controlParentName = "strategy";
		this.inherited(arguments);
		this.createChrome(this.listTools);
		this.controlParentName = "client";
		this.discoverControlParent();
	},
	rendered: function() {
		this.inherited(arguments);
		this.$.generator.node = this.$.port.hasNode();
		this.$.generator.generated = true;
		this.reset();
	},
	resizeHandler: function() {
		this.inherited(arguments);
		this.refresh();
	},
	bottomUpChanged: function() {
		this.$.generator.bottomUp = this.bottomUp;
		this.$.page0.applyStyle(this.pageBound, null);
		this.$.page1.applyStyle(this.pageBound, null);
		this.pageBound = this.bottomUp ? "bottom" : "top";
		if (this.hasNode()) {
			this.reset();
		}
	},
	multiSelectChanged: function() {
		this.$.generator.setMultiSelect(this.multiSelect);
	},
	toggleSelectedChanged: function() {
		this.$.generator.setToggleSelected(this.toggleSelected);
	},
	countChanged: function() {
		if (this.hasNode()) {
			this.updateMetrics();
		}
	},
	updateMetrics: function() {
		this.defaultPageHeight = this.rowsPerPage * (this.rowHeight || 100);
		this.pageCount = Math.ceil(this.count / this.rowsPerPage);
		this.portSize = 0;
		for (var i=0; i < this.pageCount; i++) {
			this.portSize += this.getPageHeight(i);
		}
		this.adjustPortSize();
	},
	generatePage: function(inPageNo, inTarget) {
		this.page = inPageNo;
		var r = this.$.generator.rowOffset = this.rowsPerPage * this.page;
		var rpp = this.$.generator.count = Math.min(this.count - r, this.rowsPerPage);
		var html = this.$.generator.generateChildHtml();
		inTarget.setContent(html);
		var pageHeight = inTarget.getBounds().height;
		// if rowHeight is not set, use the height from the first generated page
		if (!this.rowHeight && pageHeight > 0) {
			this.rowHeight = Math.floor(pageHeight / rpp);
			this.updateMetrics();
		}
		// update known page heights
		if (!this.fixedHeight) {
			var h0 = this.getPageHeight(inPageNo);
			if (h0 != pageHeight && pageHeight != 0) {
				this.pageHeights[inPageNo] = pageHeight;
				this.portSize += pageHeight - h0;
			}
		}
	},
	update: function(inScrollTop) {
		var updated = false;
		// get page info for position
		var pi = this.positionToPageInfo(inScrollTop);
		// zone line position
		var pos = pi.pos + this.scrollerHeight/2;
		// leap-frog zone position
		var k = Math.floor(pos/Math.max(pi.height, this.scrollerHeight) + 1/2) + pi.no;
		// which page number for page0 (even number pages)?
		var p = k % 2 == 0 ? k : k-1;
		if (this.p0 != p && this.isPageInRange(p)) {
			//this.log("update page0", p);
			this.generatePage(p, this.$.page0);
			this.positionPage(p, this.$.page0);
			this.p0 = p;
			updated = true;
		}
		// which page number for page1 (odd number pages)?
		p = k % 2 == 0 ? Math.max(1, k-1) : k;
		// position data page 1
		if (this.p1 != p && this.isPageInRange(p)) {
			//this.log("update page1", p);
			this.generatePage(p, this.$.page1);
			this.positionPage(p, this.$.page1);
			this.p1 = p;
			updated = true;
		}
		if (updated && !this.fixedHeight) {
			this.adjustBottomPage();
			this.adjustPortSize();
		}
	},
	updateForPosition: function(inPos) {
		this.update(this.calcPos(inPos));
	},
	calcPos: function(inPos) {
		return (this.bottomUp ? (this.portSize - this.scrollerHeight - inPos) : inPos);
	},
	adjustBottomPage: function() {
		var bp = this.p0 >= this.p1 ? this.$.page0 : this.$.page1;
		this.positionPage(bp.pageNo, bp);
	},
	adjustPortSize: function() {
		this.scrollerHeight = this.getBounds().height;
		var s = Math.max(this.scrollerHeight, this.portSize);
		this.$.port.applyStyle("height", s + "px");
	},
	positionPage: function(inPage, inTarget) {
		inTarget.pageNo = inPage;
		var y = this.pageToPosition(inPage);
		inTarget.applyStyle(this.pageBound, y + "px");
	},
	pageToPosition: function(inPage) {
		var y = 0;
		var p = inPage;
		while (p > 0) {
			p--;
			y += this.getPageHeight(p);
		}
		return y;
	},
	positionToPageInfo: function(inY) {
		var page = -1;
		var p = this.calcPos(inY);
		var h = this.defaultPageHeight;
		while (p >= 0) {
			page++;
			h = this.getPageHeight(page);
			p -= h;
		}
		//page = Math.min(page, this.pageCount-1);
		return {no: page, height: h, pos: p+h};
	},
	isPageInRange: function(inPage) {
		return inPage == Math.max(0, Math.min(this.pageCount-1, inPage));
	},
	getPageHeight: function(inPageNo) {
		return this.pageHeights[inPageNo] || this.defaultPageHeight;
	},
	invalidatePages: function() {
		this.p0 = this.p1 = null;
		// clear the html in our render targets
		this.$.page0.setContent("");
		this.$.page1.setContent("");
	},
	invalidateMetrics: function() {
		this.pageHeights = [];
		this.rowHeight = 0;
		this.updateMetrics();
	},
	scroll: function(inSender, inEvent) {
		var r = this.inherited(arguments);
		this.update(this.getScrollTop());
		return r;
	},
	//* @public
	scrollToBottom: function() {
		this.update(this.getScrollBounds().maxTop);
		this.inherited(arguments);
	},
	setScrollTop: function(inScrollTop) {
		this.update(inScrollTop);
		this.inherited(arguments);
		this.twiddle();
	},
	getScrollPosition: function() {
		return this.calcPos(this.getScrollTop());
	},
	setScrollPosition: function(inPos) {
		this.setScrollTop(this.calcPos(inPos));
	},
	//* Scrolls to a specific row.
	scrollToRow: function(inRow) {
		var page = Math.floor(inRow / this.rowsPerPage);
		var pageRow = inRow % this.rowsPerPage;
		var h = this.pageToPosition(page);
		// update the page
		this.updateForPosition(h);
		// call pageToPosition again and this time should return the right pos since the page info is populated
		h = this.pageToPosition(page);
		this.setScrollPosition(h);
		if (page == this.p0 || page == this.p1) {
			var rowNode = this.$.generator.fetchRowNode(inRow);
			if (rowNode) {
				// calc row offset
				var offset = rowNode.offsetTop;
				if (this.bottomUp) {
					offset = this.getPageHeight(page) - rowNode.offsetHeight - offset;
				}
				var y = this.getScrollPosition() + offset;
				this.setScrollPosition(y);
			}
		}
	},
	//* Scrolls to the beginning of the list.
	scrollToStart: function() {
		this[this.bottomUp ? "scrollToBottom" : "scrollToTop"]();
	},
	//* Scrolls to the end of the list.
	scrollToEnd: function() {
		this[this.bottomUp ? "scrollToTop" : "scrollToBottom"]();
	},
	//* Re-render the list at the current position
	refresh: function() {
		this.invalidatePages();
		this.update(this.getScrollTop());
		this.stabilize();

		//FIXME: Necessary evil for Android 4.0.4 refresh bug
		if (enyo.platform.android === 4) {
			this.twiddle();
		}
	},
	//* Re-render the list from the beginning
	reset: function() {
		this.getSelection().clear();
		this.invalidateMetrics();
		this.invalidatePages();
		this.stabilize();
		this.scrollToStart();
	},
	//* Returns the selection component (enyo.Selection) that manages the selection state for this list.
	getSelection: function() {
		return this.$.generator.getSelection();
	},
	//* Set the selection state for the given row index.
	select: function(inIndex, inData) {
		return this.getSelection().select(inIndex, inData);
	},
	//* Get the selection state for the given row index.
	isSelected: function(inIndex) {
		return this.$.generator.isSelected(inIndex);
	},
	//* Re-render the specified row. Call after making modifications to the row to force it to render.
	renderRow: function(inIndex) {
		this.$.generator.renderRow(inIndex);
	},
	//* Prepare the row to become interactive.
	prepareRow: function(inIndex) {
		this.$.generator.prepareRow(inIndex);
	},
	//* Restore the row to being non-interactive.
	lockRow: function() {
		this.$.generator.lockRow();
	},
	//* Perform a set of tasks by runnin the function inFunc on a row that require it to be interactive
	//* when those tasks are performed. Lock the row when done.
	performOnRow: function(inIndex, inFunc, inContext) {
		this.$.generator.performOnRow(inIndex, inFunc, inContext);
	},
	//* @protected
	animateFinish: function(inSender) {
		this.twiddle();
		return true;
	},
	// FIXME: Android 4.04 has issues with nested composited elements; for example, a SwipeableItem,
	// can incorrectly generate taps on its content when it has slid off the screen;
	// we address this BUG here by forcing the Scroller to "twiddle" which corrects the bug by
	// provoking a dom update.
	twiddle: function() {
		var s = this.getStrategy();
		enyo.call(s, "twiddle");
	}
});
<commit_msg>Change != to > for consistency<commit_after>/**
	A control that displays a scrolling list of rows. It is suitable for displaying very large
	lists. List is optimized such that only a small portion of the list is rendered
	at a given time. A flyweight pattern is employed such that the controls placed inside the list
	are created once but are rendered for each list item.  For this reason, it's best to use
	only simple controls in an enyo.List like enyo.Control and enyo.Image.

	## Basic Use

	A List's components block contains the controls to be used for a single row.
	This set of controls will be rendered for each row.

	The onSetupItem event allows for customization of row rendering. Here's a simple example:

		components: [
			{kind: "List", fit: true, count: 1000, onSetupItem: "setupItem", components: [
				{classes: "item", ontap: "itemTap", components: [
					{name: "name"},
					{name: "index", style: "float: right;"}
				]}
			]}
		],
		setupItem: function(inSender, inEvent) {
			// given some available data.
			var data = this.data[inEvent.index];
			// setup the controls for this item.
			this.$.name.setContent(data.name);
			this.$.index.setContent(inEvent.index);
		}

	Events fired from within list rows contain the _index_ property which can used to identify the row
	in which the event is originated from.

		itemTap: function(inSender, inEvent) {
			alert("You tapped on row: " + inEvent.index);
		}

	## Modifying List Rows

	Controls inside a list are not interactive. This means that outside the onSetupItem event,
	calling methods that would otherwise cause rendering to occur will not do so (e.g. setContent).
	A row can be forced to render by calling the renderRow(inRow) method. In addition, a row can be
	temporarily made interactive by calling the prepareRow(inRow) method. When interaction is complete, the
	lockRow() method should be called.
*/
enyo.kind({
	name: "enyo.List",
	kind: "Scroller",
	classes: "enyo-list",
	published: {
		//* The number of rows contained in the list. Note, as the amount of list data changes
		//* setRows can be called to adjust the number of rows. To re-render the list at the
		//* current position when count has changed, call the refresh() method.
		count: 0,
		//* The number of rows to be shown on a given list page segment.
		//* It is not common to need to adjust this.
		rowsPerPage: 50,
		//* Render the list such that row 0 is at the bottom of the viewport
		//* and the beginning position of the list is scrolled to the bottom.
		bottomUp: false,
		//* If true, allow multiple selections
		multiSelect: false,
		//* If true, the selected item will toggle
		toggleSelected: false,
		//* If true, the list would assume all rows have the same height for optimization
		fixedHeight: false
	},
	events: {
		//* Fired once per row at render-time, with event object: {index: <index of row>}
		onSetupItem: ""
	},
	handlers: {
		onAnimateFinish: "animateFinish"
	},
	//* @protected
	rowHeight: 0,
	listTools: [
		{name: "port", classes: "enyo-list-port enyo-border-box", components: [
			{name: "generator", kind: "FlyweightRepeater", canGenerate: false, components: [
				{tag: null, name: "client"}
			]},
			{name: "page0", allowHtml: true, classes: "enyo-list-page"},
			{name: "page1", allowHtml: true, classes: "enyo-list-page"}
		]}
	],
	create: function() {
		this.pageHeights = [];
		this.inherited(arguments);
		this.getStrategy().translateOptimized = true;
		this.bottomUpChanged();
		this.multiSelectChanged();
		this.toggleSelectedChanged();
	},
	createStrategy: function() {
		this.controlParentName = "strategy";
		this.inherited(arguments);
		this.createChrome(this.listTools);
		this.controlParentName = "client";
		this.discoverControlParent();
	},
	rendered: function() {
		this.inherited(arguments);
		this.$.generator.node = this.$.port.hasNode();
		this.$.generator.generated = true;
		this.reset();
	},
	resizeHandler: function() {
		this.inherited(arguments);
		this.refresh();
	},
	bottomUpChanged: function() {
		this.$.generator.bottomUp = this.bottomUp;
		this.$.page0.applyStyle(this.pageBound, null);
		this.$.page1.applyStyle(this.pageBound, null);
		this.pageBound = this.bottomUp ? "bottom" : "top";
		if (this.hasNode()) {
			this.reset();
		}
	},
	multiSelectChanged: function() {
		this.$.generator.setMultiSelect(this.multiSelect);
	},
	toggleSelectedChanged: function() {
		this.$.generator.setToggleSelected(this.toggleSelected);
	},
	countChanged: function() {
		if (this.hasNode()) {
			this.updateMetrics();
		}
	},
	updateMetrics: function() {
		this.defaultPageHeight = this.rowsPerPage * (this.rowHeight || 100);
		this.pageCount = Math.ceil(this.count / this.rowsPerPage);
		this.portSize = 0;
		for (var i=0; i < this.pageCount; i++) {
			this.portSize += this.getPageHeight(i);
		}
		this.adjustPortSize();
	},
	generatePage: function(inPageNo, inTarget) {
		this.page = inPageNo;
		var r = this.$.generator.rowOffset = this.rowsPerPage * this.page;
		var rpp = this.$.generator.count = Math.min(this.count - r, this.rowsPerPage);
		var html = this.$.generator.generateChildHtml();
		inTarget.setContent(html);
		var pageHeight = inTarget.getBounds().height;
		// if rowHeight is not set, use the height from the first generated page
		if (!this.rowHeight && pageHeight > 0) {
			this.rowHeight = Math.floor(pageHeight / rpp);
			this.updateMetrics();
		}
		// update known page heights
		if (!this.fixedHeight) {
			var h0 = this.getPageHeight(inPageNo);
			if (h0 != pageHeight && pageHeight > 0) {
				this.pageHeights[inPageNo] = pageHeight;
				this.portSize += pageHeight - h0;
			}
		}
	},
	update: function(inScrollTop) {
		var updated = false;
		// get page info for position
		var pi = this.positionToPageInfo(inScrollTop);
		// zone line position
		var pos = pi.pos + this.scrollerHeight/2;
		// leap-frog zone position
		var k = Math.floor(pos/Math.max(pi.height, this.scrollerHeight) + 1/2) + pi.no;
		// which page number for page0 (even number pages)?
		var p = k % 2 == 0 ? k : k-1;
		if (this.p0 != p && this.isPageInRange(p)) {
			//this.log("update page0", p);
			this.generatePage(p, this.$.page0);
			this.positionPage(p, this.$.page0);
			this.p0 = p;
			updated = true;
		}
		// which page number for page1 (odd number pages)?
		p = k % 2 == 0 ? Math.max(1, k-1) : k;
		// position data page 1
		if (this.p1 != p && this.isPageInRange(p)) {
			//this.log("update page1", p);
			this.generatePage(p, this.$.page1);
			this.positionPage(p, this.$.page1);
			this.p1 = p;
			updated = true;
		}
		if (updated && !this.fixedHeight) {
			this.adjustBottomPage();
			this.adjustPortSize();
		}
	},
	updateForPosition: function(inPos) {
		this.update(this.calcPos(inPos));
	},
	calcPos: function(inPos) {
		return (this.bottomUp ? (this.portSize - this.scrollerHeight - inPos) : inPos);
	},
	adjustBottomPage: function() {
		var bp = this.p0 >= this.p1 ? this.$.page0 : this.$.page1;
		this.positionPage(bp.pageNo, bp);
	},
	adjustPortSize: function() {
		this.scrollerHeight = this.getBounds().height;
		var s = Math.max(this.scrollerHeight, this.portSize);
		this.$.port.applyStyle("height", s + "px");
	},
	positionPage: function(inPage, inTarget) {
		inTarget.pageNo = inPage;
		var y = this.pageToPosition(inPage);
		inTarget.applyStyle(this.pageBound, y + "px");
	},
	pageToPosition: function(inPage) {
		var y = 0;
		var p = inPage;
		while (p > 0) {
			p--;
			y += this.getPageHeight(p);
		}
		return y;
	},
	positionToPageInfo: function(inY) {
		var page = -1;
		var p = this.calcPos(inY);
		var h = this.defaultPageHeight;
		while (p >= 0) {
			page++;
			h = this.getPageHeight(page);
			p -= h;
		}
		//page = Math.min(page, this.pageCount-1);
		return {no: page, height: h, pos: p+h};
	},
	isPageInRange: function(inPage) {
		return inPage == Math.max(0, Math.min(this.pageCount-1, inPage));
	},
	getPageHeight: function(inPageNo) {
		return this.pageHeights[inPageNo] || this.defaultPageHeight;
	},
	invalidatePages: function() {
		this.p0 = this.p1 = null;
		// clear the html in our render targets
		this.$.page0.setContent("");
		this.$.page1.setContent("");
	},
	invalidateMetrics: function() {
		this.pageHeights = [];
		this.rowHeight = 0;
		this.updateMetrics();
	},
	scroll: function(inSender, inEvent) {
		var r = this.inherited(arguments);
		this.update(this.getScrollTop());
		return r;
	},
	//* @public
	scrollToBottom: function() {
		this.update(this.getScrollBounds().maxTop);
		this.inherited(arguments);
	},
	setScrollTop: function(inScrollTop) {
		this.update(inScrollTop);
		this.inherited(arguments);
		this.twiddle();
	},
	getScrollPosition: function() {
		return this.calcPos(this.getScrollTop());
	},
	setScrollPosition: function(inPos) {
		this.setScrollTop(this.calcPos(inPos));
	},
	//* Scrolls to a specific row.
	scrollToRow: function(inRow) {
		var page = Math.floor(inRow / this.rowsPerPage);
		var pageRow = inRow % this.rowsPerPage;
		var h = this.pageToPosition(page);
		// update the page
		this.updateForPosition(h);
		// call pageToPosition again and this time should return the right pos since the page info is populated
		h = this.pageToPosition(page);
		this.setScrollPosition(h);
		if (page == this.p0 || page == this.p1) {
			var rowNode = this.$.generator.fetchRowNode(inRow);
			if (rowNode) {
				// calc row offset
				var offset = rowNode.offsetTop;
				if (this.bottomUp) {
					offset = this.getPageHeight(page) - rowNode.offsetHeight - offset;
				}
				var y = this.getScrollPosition() + offset;
				this.setScrollPosition(y);
			}
		}
	},
	//* Scrolls to the beginning of the list.
	scrollToStart: function() {
		this[this.bottomUp ? "scrollToBottom" : "scrollToTop"]();
	},
	//* Scrolls to the end of the list.
	scrollToEnd: function() {
		this[this.bottomUp ? "scrollToTop" : "scrollToBottom"]();
	},
	//* Re-render the list at the current position
	refresh: function() {
		this.invalidatePages();
		this.update(this.getScrollTop());
		this.stabilize();

		//FIXME: Necessary evil for Android 4.0.4 refresh bug
		if (enyo.platform.android === 4) {
			this.twiddle();
		}
	},
	//* Re-render the list from the beginning
	reset: function() {
		this.getSelection().clear();
		this.invalidateMetrics();
		this.invalidatePages();
		this.stabilize();
		this.scrollToStart();
	},
	//* Returns the selection component (enyo.Selection) that manages the selection state for this list.
	getSelection: function() {
		return this.$.generator.getSelection();
	},
	//* Set the selection state for the given row index.
	select: function(inIndex, inData) {
		return this.getSelection().select(inIndex, inData);
	},
	//* Get the selection state for the given row index.
	isSelected: function(inIndex) {
		return this.$.generator.isSelected(inIndex);
	},
	//* Re-render the specified row. Call after making modifications to the row to force it to render.
	renderRow: function(inIndex) {
		this.$.generator.renderRow(inIndex);
	},
	//* Prepare the row to become interactive.
	prepareRow: function(inIndex) {
		this.$.generator.prepareRow(inIndex);
	},
	//* Restore the row to being non-interactive.
	lockRow: function() {
		this.$.generator.lockRow();
	},
	//* Perform a set of tasks by runnin the function inFunc on a row that require it to be interactive
	//* when those tasks are performed. Lock the row when done.
	performOnRow: function(inIndex, inFunc, inContext) {
		this.$.generator.performOnRow(inIndex, inFunc, inContext);
	},
	//* @protected
	animateFinish: function(inSender) {
		this.twiddle();
		return true;
	},
	// FIXME: Android 4.04 has issues with nested composited elements; for example, a SwipeableItem,
	// can incorrectly generate taps on its content when it has slid off the screen;
	// we address this BUG here by forcing the Scroller to "twiddle" which corrects the bug by
	// provoking a dom update.
	twiddle: function() {
		var s = this.getStrategy();
		enyo.call(s, "twiddle");
	}
});
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const dotenv = require('dotenv');

const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];

const basename = path.basename(module.filename);

const db = {};

dotenv.config();
let sequelize;

if (config.url) {
  sequelize = new Sequelize(config.url);
} else {
  sequelize = new Sequelize(
    config.database,
    config.username,
    config.password,
    config
  );
}

fs
  .readdirSync(__dirname)
  .filter(file =>
    (file.indexOf('.') !== 0) &&
    (file !== basename) &&
    (file.slice(-3) === '.js'))
  .forEach((file) => {
    const model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
<commit_msg>[fix]: connection to database<commit_after>require('dotenv').config();
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];

const basename = path.basename(module.filename);

const db = {};

let sequelize;

if (config.url) {
  sequelize = new Sequelize(config.url);
} else {
  sequelize = new Sequelize(
    config.database,
    config.username,
    config.password,
    config
  );
}

fs
  .readdirSync(__dirname)
  .filter(file =>
    (file.indexOf('.') !== 0) &&
    (file !== basename) &&
    (file.slice(-3) === '.js'))
  .forEach((file) => {
    const model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
<|endoftext|>


<commit_before>var azure = require('azure-storage');
var entGen = azure.TableUtilities.entityGenerator;

function Tweet(author, message, timestamp, partitionKey, rowKey) {
    this.PartitionKey = partitionKey || Tweet.partitionKey,
    this.RowKey = rowKey || (new Date(3000, 1) - Date.now()).toString(),

    this.id = this.rowKey;
    this.author = author;
    this.message = message;
    this.timestamp = timestamp;
}

Tweet.prototype.generateEntity = function() {
    var entity = {};
    for (var propertyName in this) {
        if (Object.prototype.hasOwnProperty.call(this, propertyName)) {
            entity[propertyName] = entGen.String(this[propertyName]);
        }
    }
    return entity;
};

Tweet.partitionKey = 'tweet';

Tweet.parseEntity = function(entity) {
    return new Tweet(entity.author._, entity.message._, entity.timestamp._, entity.PartitionKey._, entity.RowKey._);
};

module.exports = Tweet;<commit_msg>update model to return proper id<commit_after>var azure = require('azure-storage');
var entGen = azure.TableUtilities.entityGenerator;

function Tweet(author, message, timestamp, partitionKey, rowKey) {
    this.PartitionKey = partitionKey || Tweet.partitionKey,
    this.RowKey = rowKey || (new Date(3000, 1) - Date.now()).toString(),

    this.id = this.RowKey;
    this.author = author;
    this.message = message;
    this.timestamp = timestamp;
}

Tweet.prototype.generateEntity = function() {
    var entity = {};
    for (var propertyName in this) {
        if (Object.prototype.hasOwnProperty.call(this, propertyName)) {
            entity[propertyName] = entGen.String(this[propertyName]);
        }
    }
    return entity;
};

Tweet.partitionKey = 'tweet';

Tweet.parseEntity = function(entity) {
    return new Tweet(entity.author._, entity.message._, entity.timestamp._, entity.PartitionKey._, entity.RowKey._);
};

module.exports = Tweet;<|endoftext|>


<commit_before>const pg = require("pg");
const AWS = require("aws-sdk");

const escape_sql_identifier = function(s) {
	/* Produce a "quoted identifier" https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS */
	/* Quoted identifiers can contain any character, except the character with code zero. (To include a double quote, write two double quotes.) */
	if (s.indexOf("\0") != -1) throw Error("invalid identifier");
	s = s.replace('"', '""');
	return '"' + s + '"'
};

exports.run = function(pg_config, kinesis_config, channel, streamName, SequenceNumber) {
	const client = new pg.Client(pg_config);
	const kinesis = new AWS.Kinesis(kinesis_config);

	const onnotify = function(msg) {
		let params = {
			StreamName: streamName,
			PartitionKey: msg.channel,
			Data: msg.payload,
			SequenceNumberForOrdering: SequenceNumber
		};

		kinesis.putRecord(params, function(err, data) {
			if (err) {
				console.error(err, err.stack);
			} else {
				SequenceNumber = data.SequenceNumber;
			}
		});
	};

	/* Set up DB connection */
	return client.connect()
		.then(() => {
			client.on("notification", onnotify);

			/* Subscribe to channel(s) */
			return client.query('LISTEN ' + escape_sql_identifier(channel));
		});
};
<commit_msg>pg-kinesis-bridge.js: Major refactor to allow multiple streams and/or multiple channels<commit_after>const pg = require("pg");
const AWS = require("aws-sdk");

const PgKinesisBridge = exports.PgKinesisBridge = function(pg_config, kinesis_config) {
	this.pgclient = new pg.Client(pg_config);
	this.kinesis = new AWS.Kinesis(kinesis_config);
	this.streams = {}; /* map from streamName to SequenceNumber */
	this.channels = {};
	this.connected = false;
};

PgKinesisBridge.prototype._onputfail = function(err) {
	console.error(err, err.stack);
};

PgKinesisBridge.prototype._onnotify = function(msg) {
	for (let streamName in this.streams) {
		let params = {
			StreamName: streamName,
			PartitionKey: msg.channel,
			Data: msg.payload,
			SequenceNumberForOrdering: this.streams[streamName]
		};

		this.kinesis.putRecord(params, (function(err, data) {
			if (err) {
				this._onputfail(err);
			} else {
				this.streams[streamName] = data.SequenceNumber;
			}
		}).bind(this));
	}
};

const escape_sql_identifier = function(s) {
	/* Produce a "quoted identifier" https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS */
	/* Quoted identifiers can contain any character, except the character with code zero. (To include a double quote, write two double quotes.) */
	if (typeof s != "string" || s.indexOf("\0") != -1) throw Error("invalid identifier");
	s = s.replace('"', '""');
	return '"' + s + '"'
};

PgKinesisBridge.prototype._listen = function(channel) {
	return this.pgclient.query('LISTEN ' + escape_sql_identifier(channel));
};

PgKinesisBridge.prototype.addChannel = function(channel) {
	this.channels[channel] = true;
	if (this.connected) {
		return this._listen(channel).then(() => void 0);
	} else {
		/* return already resolved promise */
		return Promise.resolve();
	}
};

PgKinesisBridge.prototype.addStream = function(streamName, SequenceNumber) {
	this.streams[streamName] = (SequenceNumber || null);
};

PgKinesisBridge.prototype.connect = function() {
	/* Set up DB connection */
	let promise = this.pgclient.connect()
		.then(() => {
			this.pgclient.on("notification", this._onnotify.bind(this));
			this.connected = true;
		})
	/* Subscribe to channels */
		.then(() => this.pgclient.query("begin"));
	for (let channel in this.channels) {
		promise = promise.then(() => this._listen(channel));
	}
	return promise.then(() => this.pgclient.query("commit"));
};

exports.run = function(pg_config, kinesis_config, channel, streamName, SequenceNumber) {
	let pkb = new PgKinesisBridge(pg_config, kinesis_config);
	pkb.addStream(streamName, SequenceNumber);
	pkb.addChannel(channel);
	return pkb.connect();
};
<|endoftext|>


<commit_before>'use strict';

var Support   = require(__dirname + '/../support')
  , expectsql = Support.expectsql
  , current   = Support.sequelize
  , sql       = current.dialect.QueryGenerator;

// Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation

suite(Support.getTestDialectTeaser('SQL'), function() {
  suite('removeColumn', function () {
    test('schema', function () {
      expectsql(sql.removeColumnQuery({
        schema: 'archive',
        tableName: 'user'
      }, 'email'), {
        default: 'ALTER TABLE [archive].[user] DROP COLUMN [email];',
        mysql: 'ALTER TABLE `archive.user` DROP `email`;'
      });
    });
  });
});<commit_msg>fix: dont run removeColumn tests for sqlite<commit_after>'use strict';

var Support   = require(__dirname + '/../support')
  , expectsql = Support.expectsql
  , current   = Support.sequelize
  , sql       = current.dialect.QueryGenerator;

// Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation

if (current.dialect.name !== 'sqlite') {
  suite(Support.getTestDialectTeaser('SQL'), function() {
    suite('removeColumn', function () {
      test('schema', function () {
        expectsql(sql.removeColumnQuery({
          schema: 'archive',
          tableName: 'user'
        }, 'email'), {
          mssql: 'ALTER TABLE [archive].[user] DROP [email];',
          mysql: 'ALTER TABLE `archive.user` DROP `email`;',
          postgres: 'ALTER TABLE "archive"."user" DROP COLUMN "email";',
        });
      });
    });
  });
}<|endoftext|>


<commit_before>var passport = require('passport');
var middleware = require('./middleware.js');

module.exports = function(app, io, dbConfig) {
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(middleware.redirectBasedOnLoggedInStatus);
  app.use(app.router);

  var poetsRepo = require('../repositories/poets_repository')(dbConfig);
  var auth = require('../services/authentication_service')(poetsRepo);

  var login = require('./login')(dbConfig);
  var register = require('./register')(dbConfig);
  var poems = require('./poems')(dbConfig);
  var poets = require('./poets')(dbConfig);
  var lines = require('./lines')(io, dbConfig);

  app.get('/', login.get);
  app.get('/login', login.get);
  app.post('/login', login.login);
  app.get('/logout', login.logout);

  app.get('/register', register.get);
  app.post('/register', register.create);

  app.get('/poem', poem.list);
  app.get('/poems/new', poem.createForm);
  app.get('/poems/:id', auth.verifyPoetAccess('id'), poem.edit);
  app.post('/poem', poem.create);

  app.post('/line', auth.verifyPoetAccess('poemId'), line.create);

  app.get('/poets', poets.search);
};
<commit_msg>Fix issues from merge conflict<commit_after>var passport = require('passport');
var middleware = require('./middleware.js');

module.exports = function(app, io, dbConfig) {
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(middleware.redirectBasedOnLoggedInStatus);
  app.use(app.router);

  var poetsRepo = require('../repositories/poets_repository')(dbConfig);
  var auth = require('../services/authentication_service')(poetsRepo);

  var login = require('./login')(dbConfig);
  var register = require('./register')(dbConfig);
  var poems = require('./poems')(dbConfig);
  var poets = require('./poets')(dbConfig);
  var lines = require('./lines')(io, dbConfig);

  app.get('/', login.get);
  app.get('/login', login.get);
  app.post('/login', login.login);
  app.get('/logout', login.logout);

  app.get('/register', register.get);
  app.post('/register', register.create);

  app.get('/poems', poems.list);
  app.get('/poems/new', poems.createForm);
  app.get('/poems/:id', auth.verifyPoetAccess('id'), poems.edit);
  app.post('/poems', poems.create);

  app.post('/lines', auth.verifyPoetAccess('poemId'), lines.create);

  app.get('/poets', poets.search);
};
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var path = require('path')

// This is returned as the module
var indexRoute = {
    changePage: null,
    router: router
}

router.route('/')
    .get(function (req, res) {
        res.render('main/index', {
            pageId: "Home",
            pageTitle: "Home Control",
            pageDescription: "Home Control's landing page.",
            pageCss: ['main'],
            pageScripts: ['dummyScript'],
            useBootstrap: true
        });
    });

router.route('/switcher')
    .get(isLoggedIn, function (req, res) {

        if (!req.cookies.deviceName) {
            res.redirect('/device');
        }

        res.render('main/switcher', {
            pageId: "Switcher",
            pageTitle: "Home Control Switcher",
            pageDescription: "Home Control's switcher page.",
            pageCss: ['main'],
            pageScripts: ['switcher'],
            useBootstrap: true,
            useSocket: true,
            userID: req.session.passport.user
        });
    });

router.route('/viewer')
    .get(isLoggedIn, function (req, res) {

        if (!req.cookies.deviceName) {
            res.redirect('/device');
        }

        res.render('main/viewer', {
            pageId: "Viewer",
            pageTitle: "Home Control",
            pageDescription: "Home Control's viewing page.",
            pageCss: ['main'],
            pageScripts: ['viewer'],
            useBootstrap: true,
            useSocket: true,
            userID: req.session.passport.user
        });
    });

router.post('/switch', function(req, res) {
    let action = req.body.result.action;

    // Show page intent
    if (action === 'show_page') {
        let page = req.body.result.parameters.page;
        indexRoute.changePage(page);
        res.json({
            "speech": "Showing " + page,
            "displayText": "Showing " + page,
            "source": "home.carlschriever.com",
            "data": {
                "google": {
                    "expect_user_response": false,
                }
            }
        });
    } else if (action === 'show_weather') {
        let location = req.body.result.parameters.location || 'Atlanta';
        indexRoute.changePage('weather/' + location);
        res.json({
            "speech": "Showing weather for " + location,
            "displayText": "Showing weather for " + location,
            "source": "home.carlschriever.com",
            "data": {
                "google": {
                    "expect_user_response": false,
                }
            }
        });
    } else if (action === 'activate_button') {
        let button = req.body.result.parameters.button_id;
        res.json({
            "speech": "Pressing " + button,
            "displayText": "Pressing " + button,
            "source": "home.carlschriever.com",
            "data": {
                "google": {
                    "expect_user_response": false,
                }
            }
        });
    }
});

function isLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/user/login');
}

module.exports = function(changePage) {
    indexRoute.changePage = changePage;

    return indexRoute;
};<commit_msg>Fix Device Redirect<commit_after>var express = require('express');
var router = express.Router();
var path = require('path')

// This is returned as the module
var indexRoute = {
    changePage: null,
    router: router
}

router.route('/')
    .get(function (req, res) {
        res.render('main/index', {
            pageId: "Home",
            pageTitle: "Home Control",
            pageDescription: "Home Control's landing page.",
            pageCss: ['main'],
            pageScripts: ['dummyScript'],
            useBootstrap: true
        });
    });

router.route('/switcher')
    .get(isLoggedIn, function (req, res) {

        if (!req.cookies.deviceName) {
            res.redirect('/device');
        }

        res.render('main/switcher', {
            pageId: "Switcher",
            pageTitle: "Home Control Switcher",
            pageDescription: "Home Control's switcher page.",
            pageCss: ['main'],
            pageScripts: ['switcher'],
            useBootstrap: true,
            useSocket: true,
            userID: req.session.passport.user
        });
    });

router.route('/viewer')
    .get(isLoggedIn, function (req, res) {

        if (!req.cookies.deviceName) {
            res.redirect('/device');
            return;
        }

        res.render('main/viewer', {
            pageId: "Viewer",
            pageTitle: "Home Control",
            pageDescription: "Home Control's viewing page.",
            pageCss: ['main'],
            pageScripts: ['viewer'],
            useBootstrap: true,
            useSocket: true,
            userID: req.session.passport.user
        });
    });

router.post('/switch', function(req, res) {
    let action = req.body.result.action;

    // Show page intent
    if (action === 'show_page') {
        let page = req.body.result.parameters.page;
        indexRoute.changePage(page);
        res.json({
            "speech": "Showing " + page,
            "displayText": "Showing " + page,
            "source": "home.carlschriever.com",
            "data": {
                "google": {
                    "expect_user_response": false,
                }
            }
        });
    } else if (action === 'show_weather') {
        let location = req.body.result.parameters.location || 'Atlanta';
        indexRoute.changePage('weather/' + location);
        res.json({
            "speech": "Showing weather for " + location,
            "displayText": "Showing weather for " + location,
            "source": "home.carlschriever.com",
            "data": {
                "google": {
                    "expect_user_response": false,
                }
            }
        });
    } else if (action === 'activate_button') {
        let button = req.body.result.parameters.button_id;
        res.json({
            "speech": "Pressing " + button,
            "displayText": "Pressing " + button,
            "source": "home.carlschriever.com",
            "data": {
                "google": {
                    "expect_user_response": false,
                }
            }
        });
    }
});

function isLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/user/login');
}

module.exports = function(changePage) {
    indexRoute.changePage = changePage;

    return indexRoute;
};<|endoftext|>


<commit_before>var express     = require("express");
var router      = express.Router();
var blog        = require("../models/blog");

/**
 * Render Login Page
 * if auth redirect to blogs
 */
router.get("/login", function authenticatedOrNot(req, res, next) {
    if(req.isAuthenticated()) {
        res.redirect("/admin#/blogs");
    } else {
        next();
    }}, function(req, res) {
    res.render("admin/login", { message: req.flash('error') });
});

/**
 * Logout
 */
router.get("/logout", function(req, res) {
    req.logout();

    res.redirect("/login");
});

/**
 * Render Main Page
 */
router.get("/", function(req, res) {
    blog.find(function(err, blogs) {
        if(err) throw(err);

        res.render("client/index", {
            title: "Dimitri Mikadze",
            blogs: blogs,
            desc: "Dimitri Mikadze Personal Blog",
            url: "/"
        });
    });
});

/**
 * Render blog by url param id
 */
router.get("/blog/:name/:id", function(req, res) {

    var name = req.params.name;
    var id = req.params.id;

    blog.findById(id, function(req, blog) {
       res.render("client/blog/blog", {
           title: name.replace("-", " "),
           blog: blog,
           desc: blog.short_desc,
           url: "/blog/" + name + "/" + id
       })
    });
});

// ===========================================
//                  Demos
// ===========================================

router.get("/demos/vanilla-slideshow", function(req, res) {
    res.render("demos/vanilla-slideshow.html");
});

/**
 * Download vanilla slideshow
 */
router.get("/download-vanilla-slideshow", function(req, res) {
    res.download("../client/views/demos/vanilla-slideshow.rar");
});

module.exports = router;<commit_msg>update urls<commit_after>var express     = require("express");
var router      = express.Router();
var blog        = require("../models/blog");

/**
 * Render Login Page
 * if auth redirect to blogs
 */
router.get("/login", function authenticatedOrNot(req, res, next) {
    if(req.isAuthenticated()) {
        res.redirect("/admin#/blogs");
    } else {
        next();
    }}, function(req, res) {
    res.render("admin/login", { message: req.flash('error') });
});

/**
 * Logout
 */
router.get("/logout", function(req, res) {
    req.logout();

    res.redirect("/login");
});

/**
 * Render Main Page
 */
router.get("/", function(req, res) {
    blog.find(function(err, blogs) {
        if(err) throw(err);

        res.render("client/index", {
            title: "Dimitri Mikadze",
            blogs: blogs,
            desc: "Dimitri Mikadze Personal Blog",
            url: "/"
        });
    });
});

/**
 * Render blog by url param id
 */
router.get("/blog/programming/:name/:id", function(req, res) {

    var name = req.params.name;
    var id = req.params.id;

    blog.findById(id, function(req, blog) {
       res.render("client/blog/blog", {
           title: name.replace("-", " "),
           blog: blog,
           desc: blog.short_desc,
           url: "/blog/" + name + "/" + id
       })
    });
});

// ===========================================
//                  Demos
// ===========================================

router.get("/demos/vanilla-slideshow", function(req, res) {
    res.render("demos/vanilla-slideshow.html");
});

/**
 * Download vanilla slideshow
 */
router.get("/download-vanilla-slideshow", function(req, res) {
    res.download("../client/views/demos/vanilla-slideshow.rar");
});

module.exports = router;<|endoftext|>


<commit_before>
module.exports = function(req, res, next) {
  var readOnly = false;
  console.log('req', req);
  if (req.url !== '/admin') {
    readOnly = true;
  }

  res.render('index', {
    readOnly: readOnly
  }, function(err, html) {
    if (err) return res.status(500).send(err);

    return res.send(html);
  });
};<commit_msg>Remove console log<commit_after>
module.exports = function(req, res, next) {
  var readOnly = false;
  if (req.url !== '/admin') {
    readOnly = true;
  }

  res.render('index', {
    readOnly: readOnly
  }, function(err, html) {
    if (err) return res.status(500).send(err);

    return res.send(html);
  });
};
<|endoftext|>


<commit_before>
  // app.use('/api/dashboards', {
  //   find (params) {
  //     return Promise.resolve({})
  //   }
  // })
<commit_msg>remove unused file<commit_after><|endoftext|>


<commit_before>var _ = require('lodash'),
  dom = require('./../dom'),
  cache = require('./cache'),
  db = require('./db'),
  references = require('../references'),
  site = require('./../site'),
  refProp = references.referenceProperty,
  pagesRoute = '/pages/',
  urisRoute = '/uris/',
  schemaKeywords = ['_ref', '_groups'],
  knownExtraFields = ['_ref', '_schema'],
  bannedFields = ['_self', '_components', '_pageRef', '_pageData', '_version', '_refs', 'layout', 'template'];

/**
 * Cloning removes extra properties like _schema from standard types like Array, because we're doing a bad thing.
 *
 * This function adds them back.
 *
 * @param {object} data
 * @returns {object}
 */
function addDeviantArraySchemas(data) {
  _.each(data._schema, function (fieldDefinition, fieldName) {
    var value = data[fieldName];

    if (_.isArray(value)) {
      value._schema = fieldDefinition;
    }
  });
  return data;
}

/**
 * Do validation
 *
 * @param {object} data
 * @param {schema} schema
 * @returns {Array}
 */
function validate(data, schema) {
  var errors = [],
    uri = data[refProp],
    keys = Object.keys(data),
    groupFields = schema._groups && Object.keys(schema._groups) || [],
    fields = Object.keys(schema).concat(knownExtraFields).concat(groupFields),
    foundBannedFields = _.intersection(bannedFields, keys),
    unknownFields = _.select(keys, function (key) { return !_.contains(fields, key); });

  if (!uri) {
    errors.push(new Error('Cannot save data without ' + refProp));
  }

  if (foundBannedFields.length) {
    errors.push(new Error('Banned fields found in data: ' + foundBannedFields.toString()));
  }

  if (unknownFields.length) {
    // todo: we need to decide:

    // be forgiving; they meant well
    _.each(unknownFields, function (unknownField) {
      delete data[unknownField];
    });

    // unforgiving: errors.push(new Error('Unknown fields found in data: ' + unknownFields.toString()));
  }

  // block unusual formatting of data (so the errors_for saving past this point are consistent)
  _.each(fields, function (fieldName) {
    var str,
      value = data[fieldName];

    // everything that is not a schemaKeyword must be object-like (object, array, etc.)
    if (!_.contains(schemaKeywords, fieldName) && typeof value !== 'object') {
      str = 'Amphora-style data (raw value) found in ' + fieldName + '; please use ClayKiln style (value, _schema) instead.';
      errors.push(new Error(str));
    }
  });

  return errors;
}

/**
 * Update data for a component.
 *
 * Note: try to operate on full objects with schemas so we don't have to lookup the schema for validation.
 *
 * @param {object} data  data that will be saved
 * @param {string} data._ref  uri to save
 * @param {object} [data._schema]  schema to validate against (optional; we can look this up)
 * @returns {Promise}
 */
function save(data) {
  var uri = data[refProp],
    schemaPromise = data._schema && Promise.resolve(data._schema) || cache.getSchema(uri);

  // get the schema and validate data
  return schemaPromise.then(function (schema) {
    var validationErrors = validate(data, schema);

    if (validationErrors.length) {
      throw new Error(validationErrors);
    } else {
      return cache.saveThrough(data);
    }
  });
}

/**
 * Update data for a part of a component.
 * @param {object} data
 * @param {string} data._ref  uri to save
 * @returns {Promise}
 */
function savePartial(data) {
  var uri = data[refProp];

  // get the old version of the data, and fill in all the missing information
  return cache.getData(uri).then(function (oldData) {
    return save(_.defaults(data, oldData));
  });
}

/**
 * Get page reference from current location
 * @param {string} [location]
 * @returns {Promise}
 */
function getUriDestination(location) {
  var prefix;

  if (_.isString(location)) {
    return db.getText(location).then(function (result) {
      if (_.contains(result, urisRoute)) {
        result = getUriDestination(result);
      }

      return result;
    });
  } else {
    prefix = site.get('prefix');
    return getUriDestination(prefix + urisRoute + btoa(dom.uri()));
  }
}

/**
 * @param {string} uri e.g. localhost.dev.nymag.biz/pages/U7V8okzAAAA=.html
 * @returns {string} e.g. localhost.dev.nymag.biz/pages/U7V8okzAAAA=
 */
function removeExtension(uri) {
  return uri.replace(/\.(html|json)$/i, '');
}

/**
 * @param {string} uri
 * @returns {string}
 */
function removeVersion(uri) {
  return uri.split('@')[0];
}

/**
 * @param {string} uri
 * @returns {string}
 */
function pathOnly(uri) {
  return removeVersion(removeExtension(uri));
}

/**
 * Publish current page's saved data.
 *
 * Pages don't have schemas or validation (later?), so save directly to db.
 *
 * @returns {Promise.string}
 */
function publishPage() {
  var uri = dom.uri(),
    isBarePage = uri.indexOf(pagesRoute) > -1,
    pageRefPromise = isBarePage ? Promise.resolve(uri) : getUriDestination();

  return pageRefPromise.then(function (pageReference) {
    var pageUri = pathOnly(pageReference);

    return cache.getDataOnly(pageUri).then(function (data) {
      // pages don't have schemas or validation (later?)
      return db.save(pageUri + '@published', _.cloneDeep(data));
    });
  });
}

/**
 * Get a url for the new page that was just created, including protocol and port
 *
 * @param {string} uri
 * @returns {string}
 */
function getNewPageUrl(uri) {
  return site.addProtocol(site.addPort(uri + '.html?edit=true'));
}

/**
 * create a new page, cloning the current page
 * @returns {Promise}
 */
function createPage() {
  var prefix = site.get('prefix'),
    newPageUri = prefix + pagesRoute + 'new';

  return cache.getDataOnly(newPageUri).then(function (data) {
    return db.create(prefix + pagesRoute, data).then(function (res) {
      location.href = getNewPageUrl(res[refProp]);
    });
  });
}

/**
 * Create a new component.
 *
 * Assumes creation is happening at current site prefix.
 *
 * @param {string} name     The name of the component.
 * @param {object} [data]   Data to save.
 * @returns {Promise}
 */
function createComponent(name, data) {
  var base = site.get('prefix') + '/components/' + name,
    instance = base + '/instances';

  if (data) {
    return cache.createThrough(instance, data);
  } else {
    return cache.getDataOnly(base) // create component with base JSON from bootstrap.
      .then(function (baseJson) {
        return cache.createThrough(instance, baseJson);
      });
  }
}

/**
 * Remove a component from a list.
 * @param {object}  opts
 * @param {Element} opts.el          The component to be removed.
 * @param {string}  opts.ref         The ref of the component to be removed.
 * @param {string}  opts.parentField
 * @param {string}  opts.parentRef
 * @returns {Promise}
 */
function removeFromParentList(opts) {
  var el = opts.el,
    ref = opts.ref,
    parentRef = opts.parentRef,
    parentField = opts.parentField;

  return cache.getData(parentRef).then(function (parentData) {
    var index,
      item = {};

    parentData = _.cloneDeep(parentData);
    item[refProp] = ref;
    index = _.findIndex(parentData[parentField], item);
    parentData[parentField].splice(index, 1); // remove component from parent data
    dom.removeElement(el); // remove component from DOM
    return save(parentData);
  });
}

/**
 * Add a component to the parent list data. If prevRef is not provided, adds to the end of the list.
 * @param {object} opts
 * @param {string} opts.ref
 * @param {string} [opts.prevRef]     The ref of the item to insert after.
 * @param {string} opts.parentField
 * @param {string} opts.parentRef
 * @returns {Promise} Promise resolves to new component Element.
 */
function addToParentList(opts) {
  var ref = opts.ref,
    prevRef = opts.prevRef,
    parentField = opts.parentField,
    parentRef = opts.parentRef;

  return cache.getData(parentRef).then(function (parentData) {
    var prevIndex,
      prevItem = {},
      item = {};

    parentData = _.cloneDeep(parentData);
    item[refProp] = ref;
    if (prevRef) {
      // Add to specific position in the list.
      prevItem[refProp] = prevRef;
      prevIndex = _.findIndex(parentData[parentField], prevItem);
      parentData[parentField].splice(prevIndex + 1, 0, item);
    } else {
      // Add to end of list.
      parentData[parentField].push(item);
    }

    return save(parentData)
      .then(db.getHTML.bind(null, ref));
  });
}

/**
 * The sad state is that people think they can write to anything in JavaScript without consequence.  For those people,
 * these functions exist.
 *
 * No caching in the world will save them.
 *
 * @param {string} uri
 * @returns {object}
 */
function getData(uri) {
  return cache.getData(uri).then(_.cloneDeep).then(addDeviantArraySchemas);
}

/**
 * The sad state is that people think they can write to anything in JavaScript without consequence.  For those people,
 * these functions exist.
 *
 * No caching in the world will save them.
 *
 * @param {string} uri
 * @returns {object}
 */
function getDataOnly(uri) {
  return cache.getDataOnly(uri).then(_.cloneDeep).then(addDeviantArraySchemas);
}

// Expose main actions (alphabetical!)
module.exports = {
  // Please use these.  They should be discrete actions that should be well tested.
  addToParentList: addToParentList,
  createComponent: createComponent,
  createPage: createPage,
  getUriDestination: getUriDestination,
  publishPage: publishPage,
  removeFromParentList: removeFromParentList,
  savePartial: savePartial,
  save: save,

  // Please stop using these.  If you use these, we don't trust you.  Do you trust yourself?
  getData: getData,
  getDataOnly: getDataOnly,
  getSchema: cache.getSchema // No one should _ever_ be editing this, so pass them the read-only.  Kill them dead.
};
<commit_msg>remove-ref<commit_after>var _ = require('lodash'),
  dom = require('./../dom'),
  cache = require('./cache'),
  db = require('./db'),
  references = require('../references'),
  site = require('./../site'),
  refProp = references.referenceProperty,
  pagesRoute = '/pages/',
  urisRoute = '/uris/',
  schemaKeywords = ['_ref', '_groups'],
  knownExtraFields = ['_ref', '_schema'],
  bannedFields = ['_self', '_components', '_pageRef', '_pageData', '_version', '_refs', 'layout', 'template'];

/**
 * Cloning removes extra properties like _schema from standard types like Array, because we're doing a bad thing.
 *
 * This function adds them back.
 *
 * @param {object} data
 * @returns {object}
 */
function addDeviantArraySchemas(data) {
  _.each(data._schema, function (fieldDefinition, fieldName) {
    var value = data[fieldName];

    if (_.isArray(value)) {
      value._schema = fieldDefinition;
    }
  });
  return data;
}

/**
 * Do validation
 *
 * @param {object} data
 * @param {schema} schema
 * @returns {Array}
 */
function validate(data, schema) {
  var errors = [],
    uri = data[refProp],
    keys = Object.keys(data),
    groupFields = schema._groups && Object.keys(schema._groups) || [],
    fields = Object.keys(schema).concat(knownExtraFields).concat(groupFields),
    foundBannedFields = _.intersection(bannedFields, keys),
    unknownFields = _.select(keys, function (key) { return !_.contains(fields, key); });

  if (!uri) {
    errors.push(new Error('Cannot save data without ' + refProp));
  }

  if (foundBannedFields.length) {
    errors.push(new Error('Banned fields found in data: ' + foundBannedFields.toString()));
  }

  if (unknownFields.length) {
    // todo: we need to decide:

    // be forgiving; they meant well
    _.each(unknownFields, function (unknownField) {
      delete data[unknownField];
    });

    // unforgiving: errors.push(new Error('Unknown fields found in data: ' + unknownFields.toString()));
  }

  // block unusual formatting of data (so the errors_for saving past this point are consistent)
  _.each(fields, function (fieldName) {
    var str,
      value = data[fieldName];

    // everything that is not a schemaKeyword must be object-like (object, array, etc.)
    if (!_.contains(schemaKeywords, fieldName) && typeof value !== 'object') {
      str = 'Amphora-style data (raw value) found in ' + fieldName + '; please use ClayKiln style (value, _schema) instead.';
      errors.push(new Error(str));
    }
  });

  return errors;
}

/**
 * Update data for a component.
 *
 * Note: try to operate on full objects with schemas so we don't have to lookup the schema for validation.
 *
 * @param {object} data  data that will be saved
 * @param {string} data._ref  uri to save
 * @param {object} [data._schema]  schema to validate against (optional; we can look this up)
 * @returns {Promise}
 */
function save(data) {
  var uri = data[refProp],
    schemaPromise = data._schema && Promise.resolve(data._schema) || cache.getSchema(uri);

  // get the schema and validate data
  return schemaPromise.then(function (schema) {
    var validationErrors = validate(data, schema);

    if (validationErrors.length) {
      throw new Error(validationErrors);
    } else {
      return cache.saveThrough(data);
    }
  });
}

/**
 * Update data for a part of a component.
 * @param {object} data
 * @param {string} data._ref  uri to save
 * @returns {Promise}
 */
function savePartial(data) {
  var uri = data[refProp];

  // get the old version of the data, and fill in all the missing information
  return cache.getData(uri).then(function (oldData) {
    return save(_.defaults(data, oldData));
  });
}

/**
 * Get page reference from current location
 * @param {string} [location]
 * @returns {Promise}
 */
function getUriDestination(location) {
  var prefix;

  if (_.isString(location)) {
    return db.getText(location).then(function (result) {
      if (_.contains(result, urisRoute)) {
        result = getUriDestination(result);
      }

      return result;
    });
  } else {
    prefix = site.get('prefix');
    return getUriDestination(prefix + urisRoute + btoa(dom.uri()));
  }
}

/**
 * @param {string} uri e.g. localhost.dev.nymag.biz/pages/U7V8okzAAAA=.html
 * @returns {string} e.g. localhost.dev.nymag.biz/pages/U7V8okzAAAA=
 */
function removeExtension(uri) {
  return uri.replace(/\.(html|json)$/i, '');
}

/**
 * @param {string} uri
 * @returns {string}
 */
function removeVersion(uri) {
  return uri.split('@')[0];
}

/**
 * @param {string} uri
 * @returns {string}
 */
function pathOnly(uri) {
  return removeVersion(removeExtension(uri));
}

/**
 * Publish current page's saved data.
 *
 * Pages don't have schemas or validation (later?), so save directly to db.
 *
 * @returns {Promise.string}
 */
function publishPage() {
  var uri = dom.uri(),
    isBarePage = uri.indexOf(pagesRoute) > -1,
    pageRefPromise = isBarePage ? Promise.resolve(uri) : getUriDestination();

  return pageRefPromise.then(function (pageReference) {
    var pageUri = pathOnly(pageReference);

    return cache.getDataOnly(pageUri).then(function (data) {
      // pages don't have schemas or validation (later?)
      return db.save(pageUri + '@published', _.omit(data, '_ref'));
    });
  });
}

/**
 * Get a url for the new page that was just created, including protocol and port
 *
 * @param {string} uri
 * @returns {string}
 */
function getNewPageUrl(uri) {
  return site.addProtocol(site.addPort(uri + '.html?edit=true'));
}

/**
 * create a new page, cloning the current page
 * @returns {Promise}
 */
function createPage() {
  var prefix = site.get('prefix'),
    newPageUri = prefix + pagesRoute + 'new';

  return cache.getDataOnly(newPageUri).then(function (data) {
    return db.create(prefix + pagesRoute, _.omit(data, '_ref')).then(function (res) {
      location.href = getNewPageUrl(res[refProp]);
    });
  });
}

/**
 * Create a new component.
 *
 * Assumes creation is happening at current site prefix.
 *
 * @param {string} name     The name of the component.
 * @param {object} [data]   Data to save.
 * @returns {Promise}
 */
function createComponent(name, data) {
  var base = site.get('prefix') + '/components/' + name,
    instance = base + '/instances';

  if (data) {
    return cache.createThrough(instance, data);
  } else {
    return cache.getDataOnly(base) // create component with base JSON from bootstrap.
      .then(function (baseJson) {
        return cache.createThrough(instance, baseJson);
      });
  }
}

/**
 * Remove a component from a list.
 * @param {object}  opts
 * @param {Element} opts.el          The component to be removed.
 * @param {string}  opts.ref         The ref of the component to be removed.
 * @param {string}  opts.parentField
 * @param {string}  opts.parentRef
 * @returns {Promise}
 */
function removeFromParentList(opts) {
  var el = opts.el,
    ref = opts.ref,
    parentRef = opts.parentRef,
    parentField = opts.parentField;

  return cache.getData(parentRef).then(function (parentData) {
    var index,
      item = {};

    parentData = _.cloneDeep(parentData);
    item[refProp] = ref;
    index = _.findIndex(parentData[parentField], item);
    parentData[parentField].splice(index, 1); // remove component from parent data
    dom.removeElement(el); // remove component from DOM
    return save(parentData);
  });
}

/**
 * Add a component to the parent list data. If prevRef is not provided, adds to the end of the list.
 * @param {object} opts
 * @param {string} opts.ref
 * @param {string} [opts.prevRef]     The ref of the item to insert after.
 * @param {string} opts.parentField
 * @param {string} opts.parentRef
 * @returns {Promise} Promise resolves to new component Element.
 */
function addToParentList(opts) {
  var ref = opts.ref,
    prevRef = opts.prevRef,
    parentField = opts.parentField,
    parentRef = opts.parentRef;

  return cache.getData(parentRef).then(function (parentData) {
    var prevIndex,
      prevItem = {},
      item = {};

    parentData = _.cloneDeep(parentData);
    item[refProp] = ref;
    if (prevRef) {
      // Add to specific position in the list.
      prevItem[refProp] = prevRef;
      prevIndex = _.findIndex(parentData[parentField], prevItem);
      parentData[parentField].splice(prevIndex + 1, 0, item);
    } else {
      // Add to end of list.
      parentData[parentField].push(item);
    }

    return save(parentData)
      .then(db.getHTML.bind(null, ref));
  });
}

/**
 * The sad state is that people think they can write to anything in JavaScript without consequence.  For those people,
 * these functions exist.
 *
 * No caching in the world will save them.
 *
 * @param {string} uri
 * @returns {object}
 */
function getData(uri) {
  return cache.getData(uri).then(_.cloneDeep).then(addDeviantArraySchemas);
}

/**
 * The sad state is that people think they can write to anything in JavaScript without consequence.  For those people,
 * these functions exist.
 *
 * No caching in the world will save them.
 *
 * @param {string} uri
 * @returns {object}
 */
function getDataOnly(uri) {
  return cache.getDataOnly(uri).then(_.cloneDeep).then(addDeviantArraySchemas);
}

// Expose main actions (alphabetical!)
module.exports = {
  // Please use these.  They should be discrete actions that should be well tested.
  addToParentList: addToParentList,
  createComponent: createComponent,
  createPage: createPage,
  getUriDestination: getUriDestination,
  publishPage: publishPage,
  removeFromParentList: removeFromParentList,
  savePartial: savePartial,
  save: save,

  // Please stop using these.  If you use these, we don't trust you.  Do you trust yourself?
  getData: getData,
  getDataOnly: getDataOnly,
  getSchema: cache.getSchema // No one should _ever_ be editing this, so pass them the read-only.  Kill them dead.
};
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var article = require("article");
var kanye = require("../../kanye");
var request = require("request");

var STATE = {
    getState: function(number) {
        if (!this[number]) {
            this[number] = {};
        }
        return this[number];
    }
};

var isImage = function(url) {
    return  url.indexOf("jpg") != -1 ||
            url.indexOf("jpeg") != -1 ||
            url.indexOf("gif") != -1 ||
            url.indexOf("png") != -1;
};

var makeRedditRequest = function(page, number, res) {
    console.log("reddit request from " + page);
    request("http://reddit.com/top.json?limit=100",
        function(error, response, body) {
            var json = JSON.parse(body);

            var state = STATE.getState(number);
            state.page = page;

            var response = "";
            var num = 1;
            json.data.children = json.data.children.splice(page, 5);
            console.log(json.data.children.length);
            for (var x in json.data.children) {
                var child = json.data.children[x].data;
                var title = child.title;
                response += (num++) + ".) " + title + "\n";
            }

            console.log(response);
            res.send(JSON.stringify({
                message: response,
                number: number
            }));
            res.status(200).end();
    });
};

// The main app can hit this when an SMS is received
app.get('/sms', function(req, res) {
    var message = req.query.message;
    var number = req.query.number;

    // If it's a number
    if (!isNaN(message)) {
        var num = parseInt(message);
        request("http://reddit.com/top.json?limit=100",
            function(error, response, body) {
                console.log("Here");
                var json = JSON.parse(body);

                var child = json.data.children[num-1].data;
                var url = child.url;

                if (isImage(url)) {
                    res.send(JSON.stringify({
                        media: url,
                        number: number
                    }));
                    res.status(200).end();
                } else {
                    request(url).pipe(article(url, function(err, result) {
                        if (err) {
                            res.send(JSON.stringify({
                                message: "Sorry, couldn't display that post.",
                                number: number
                            }));
                            return;
                        }
                        console.log(result.text);
                        res.send({
                            message: result.text.substring(0, 1500),
                            number: number
                        });
                        res.status(200).end();
                    }));
                }
        });

        return;
    }

    // The command reddit always starts at page 0
    if (message.trim().toLowerCase() == "reddit") {
        makeRedditRequest(0, number, res);

        return;
    }

    if (message.trim().toLowerCase() == "more" ||
            message.trim().toLowerCase() == "next") {
        console.log("pagination");
        var state = STATE.getState(number);
        console.log(state);
        if (typeof state.page == undefined) {
            res.status(400).end();
            return;
        }

        makeRedditRequest(state.page + 5, number, res);

        return;
    }

    // We should only get here if we couldnt handle the message
    res.status(400).end();
    return;
});

app.listen(3002);
<commit_msg>Reddit pagination<commit_after>var express = require('express');
var app = express();
var article = require("article");
var kanye = require("../../kanye");
var request = require("request");

var STATE = {
    getState: function(number) {
        if (!this[number]) {
            this[number] = {};
        }
        return this[number];
    }
};

var isImage = function(url) {
    return  url.indexOf("jpg") != -1 ||
            url.indexOf("jpeg") != -1 ||
            url.indexOf("gif") != -1 ||
            url.indexOf("png") != -1;
};

var makeRedditRequest = function(page, number, res) {
    console.log("reddit request from " + page);
    request("http://reddit.com/top.json?limit=100",
        function(error, response, body) {
            var json = JSON.parse(body);

            var state = STATE.getState(number);
            state.page = page;

            var response = "";
            var num = page + 1;
            json.data.children = json.data.children.splice(page, 5);
            console.log(json.data.children.length);
            for (var x in json.data.children) {
                var child = json.data.children[x].data;
                var title = child.title;
                response += (num++) + ".) " + title + "\n";
            }

            console.log(response);
            res.send(JSON.stringify({
                message: response,
                number: number
            }));
            res.status(200).end();
    });
};

// The main app can hit this when an SMS is received
app.get('/sms', function(req, res) {
    var message = req.query.message;
    var number = req.query.number;

    // If it's a number
    if (!isNaN(message)) {
        var num = parseInt(message);
        request("http://reddit.com/top.json?limit=100",
            function(error, response, body) {
                console.log("Here");
                var json = JSON.parse(body);

                var child = json.data.children[num-1].data;
                var url = child.url;

                if (isImage(url)) {
                    res.send(JSON.stringify({
                        media: url,
                        number: number
                    }));
                    res.status(200).end();
                } else {
                    request(url).pipe(article(url, function(err, result) {
                        if (err) {
                            res.send(JSON.stringify({
                                message: "Sorry, couldn't display that post.",
                                number: number
                            }));
                            return;
                        }
                        console.log(result.text);
                        res.send({
                            message: result.text.substring(0, 1500),
                            number: number
                        });
                        res.status(200).end();
                    }));
                }
        });

        return;
    }

    // The command reddit always starts at page 0
    if (message.trim().toLowerCase() == "reddit") {
        makeRedditRequest(0, number, res);

        return;
    }

    if (message.trim().toLowerCase() == "more" ||
            message.trim().toLowerCase() == "next") {
        console.log("pagination");
        var state = STATE.getState(number);
        console.log(state);
        if (typeof state.page == undefined) {
            res.status(400).end();
            return;
        }

        makeRedditRequest(state.page + 5, number, res);

        return;
    }

    // We should only get here if we couldnt handle the message
    res.status(400).end();
    return;
});

app.listen(3002);
<|endoftext|>