<commit_before>import ArchiveOfferModal from '../components/modals/offers/archive';
import Controller, {inject as controller} from '@ember/controller';
import UnarchiveOfferModal from '../components/modals/offers/unarchive';
import config from 'ghost-admin/config/environment';
import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard';
import {action} from '@ember/object';
import {getSymbol} from 'ghost-admin/utils/currency';
import {ghPriceAmount} from '../helpers/gh-price-amount';
import {inject as service} from '@ember/service';
import {slugify} from '@tryghost/string';
import {task} from 'ember-concurrency';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

export default class OffersController extends Controller {
    @controller offers;
    @service config;
    @service settings;
    @service store;
    @service modals;
    @service feature;
    @service membersUtils;
    @service notifications;

    @tracked cadences = [];
    @tracked tiers = [];
    @tracked portalPreviewUrl = '';
    @tracked showUnsavedChangesModal = false;

    @tracked defaultSiteUrl = this.config.get('blogUrl');

    @tracked durations = [
        {
            label: 'Forever',
            duration: 'forever'
        },
        {
            label: 'First-payment',
            duration: 'once'
        },
        {
            label: 'Multiple-months',
            duration: 'repeating'
        }
    ];

    @tracked offertypes = [
        {
            label: '%',
            offertype: 'percent'
        },
        {
            label: 'USD',
            offertype: 'fixed'
        }
    ];

    @tracked defaultProps = null;
    @tracked isDisplayTitleEdited = false;
    @tracked isOfferCodeEdited = false;

    leaveScreenTransition = null;
    portalPreviewGuid = Date.now().valueOf();

    constructor() {
        super(...arguments);
        if (this.isTesting === undefined) {
            this.isTesting = config.environment === 'test';
        }
    }

    get offer() {
        return this.model;
    }

    set offer(offer) {
        this.model = offer;
    }

    get scratchOffer() {
        return {
            ...this.offer
        };
    }

    get isTrialOffer() {
        return this.offer?.type === 'trial';
    }

    get isDiscountOffer() {
        return this.offer?.type !== 'trial';
    }

    get cadence() {
        if (this.offer.tier && this.offer.cadence) {
            const tier = this.tiers.findBy('id', this.offer.tier.id);
            return `${this.offer.tier.id}-${this.offer.cadence}-${tier?.currency}`;
        } else if (this.defaultProps) {
            const tier = this.tiers.findBy('id', this.defaultProps.tier.id);
            return `${this.defaultProps.tier.id}-${this.defaultProps.cadence}-${tier?.currency}`;
        }
        return '';
    }

    get isDiscountSectionDisabled() {
        return !this.offer.isNew;
    }

    // Tasks -------------------------------------------------------------------

    @task({drop: true})
    *fetchTiers() {
        this.tiers = yield this.store.query('tier', {filter: 'type:paid+active:true', include: 'monthly_price,yearly_price'});
        this.tiers = this.tiers.filter((d) => {
            return d.monthlyPrice && d.yearlyPrice;
        });
        const cadences = [];
        this.tiers.forEach((tier) => {
            let monthlyLabel;
            let yearlyLabel;
            const tierCurrency = tier.currency;
            const tierCurrencySymbol = tierCurrency.toUpperCase();
            monthlyLabel = `${tier.name} - Monthly (${ghPriceAmount(tier.monthlyPrice)} ${tierCurrencySymbol})`;
            yearlyLabel = `${tier.name} - Yearly (${ghPriceAmount(tier.yearlyPrice)} ${tierCurrencySymbol})`;

            cadences.push({
                label: monthlyLabel,
                name: `${tier.id}-month-${tierCurrency}`
            });

            cadences.push({
                label: yearlyLabel,
                name: `${tier.id}-year-${tierCurrency}`
            });
        });
        this.cadences = cadences;
        const defaultCadence = this.cadences[0]?.name;
        const [,interval, defaultCurrency] = (defaultCadence || '').split('-');

        this.updateDurations(interval);
        if (this.offer && !this.offer.tier) {
            this.defaultProps = {};
            this.updateCadence(defaultCadence, this.defaultProps);
            this.updatePortalPreview({forceRefresh: false});
        } else if (defaultCadence) {
            this.offertypes = [
                {
                    label: '%',
                    offertype: 'percent'
                },
                {
                    label: defaultCurrency.toUpperCase(),
                    offertype: 'fixed'
                }
            ];
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @task({drop: true})
    *copyOfferUrl() {
        copyTextToClipboard(this.offerUrl);
        yield timeout(this.isTesting ? 50 : 500);
        return true;
    }

    @task({drop: true})
    *saveTask() {
        let {offer} = this;

        if (!offer.tier && this.defaultProps) {
            this.offer.tier = {
                id: this.defaultProps?.tier.id
            };
            this.offer.cadence = this.defaultProps.cadence;
            this.offer.currency = this.defaultProps.currency;
        }

        try {
            yield this.offer.validate();
            yield offer.save();

            // replace 'offer.new' route with 'offer' route
            this.replaceRoute('offer', offer);

            return offer;
        } catch (error) {
            if (error) {
                this.notifications.showAPIError(error, {key: 'offer.save'});
            }
        }
    }

    @task
    *fetchOfferTask(offerId) {
        this.isLoading = true;

        this.offer = yield this.store.queryRecord('offer', {
            id: offerId
        });

        this.isLoading = false;
    }

    @action
    portalPreviewInserted(iframe) {
        this.portalPreviewIframe = iframe;

        if (!this.portalMessageListener) {
            this.portalMessageListener = (event) => {
                const resizeEvents = ['portal-ready', 'portal-preview-updated'];
                if (resizeEvents.includes(event.data.type) && event.data.payload?.height && this.portalPreviewIframe?.parentNode) {
                    this.portalPreviewIframe.parentNode.style.height = `${event.data.payload.height}px`;
                }
            };

            window.addEventListener('message', this.portalMessageListener, true);
        }
    }

    @action
    portalPreviewDestroyed() {
        this.portalPreviewIframe = null;

        if (this.portalMessageListener) {
            window.removeEventListener('message', this.portalMessageListener);
        }
    }

    @action
    updatePortalPreview({forceRefresh} = {forceRefresh: false}) {
        const newUrl = new URL(this.membersUtils.getOfferPortalPreviewUrl({
            name: this.offer.name || 'No Name',
            code: this.offer.code || 'no-code',
            displayTitle: this.offer.displayTitle,
            displayDescription: this.offer.displayDescription,
            type: this.offer.type,
            cadence: this.offer.cadence || this.defaultProps?.cadence,
            amount: this.offer.amount,
            duration: this.offer.duration,
            durationInMonths: this.offer.durationInMonths,
            currency: this.offer.currency || this.defaultProps?.currency,
            status: this.offer.currency,
            tierId: this.offer?.tier?.id || this.defaultProps?.tier?.id
        }));

        if (forceRefresh) {
            this.portalPreviewGuid = Date.now().valueOf();
        }
        newUrl.searchParams.set('v', `${this.portalPreviewGuid}`);

        this.portalPreviewUrl = newUrl;
    }

    @action
    save() {
        return this.saveTask.perform();
    }

    @action
    leaveScreen() {
        this.offer.rollbackAttributes();
        return this.leaveScreenTransition.retry();
    }

    @action
    toggleUnsavedChangesModal(transition) {
        let leaveTransition = this.leaveScreenTransition;

        if (!transition && this.showUnsavedChangesModal) {
            this.leaveScreenTransition = null;
            this.showUnsavedChangesModal = false;
            return;
        }

        if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
            this.leaveScreenTransition = transition;

            // if a save is running, wait for it to finish then transition
            if (this.save.isRunning) {
                return this.save.last.then(() => {
                    transition.retry();
                });
            }

            // we genuinely have unsaved data, show the modal
            this.showUnsavedChangesModal = true;
        }
    }

    @action
    setup() {
        this.fetchTiers.perform();
    }

    @action
    setProperty(propKey, value) {
        this._saveOfferProperty(propKey, value);
    }

    @action
    setDiscountType(discountType) {
        if (!this.isDiscountSectionDisabled) {
            const amount = this.offer.amount || 0;
            this._saveOfferProperty('type', discountType);
            if (this.offer.type === 'fixed' && this.offer.amount !== '') {
                this.offer.amount = amount * 100;
            } else if (this.offer.amount !== '') {
                this.offer.amount = amount / 100;
            }
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @action
    setDiscountAmount(e) {
        let amount = e.target.value;
        if (this.offer.type === 'fixed' && amount !== '') {
            amount = parseFloat(amount) * 100;
        }
        this._saveOfferProperty('amount', amount);
    }

    @action
    setTrialDuration(e) {
        let amount = e.target.value;
        if (amount !== '') {
            amount = parseInt(amount);
        }
        this._saveOfferProperty('amount', amount);
    }

    @action
    setOfferName(e) {
        this._saveOfferProperty('name', e.target.value);
        if (!this.isDisplayTitleEdited && this.offer.isNew) {
            this._saveOfferProperty('displayTitle', e.target.value);
        }

        if (!this.isOfferCodeEdited && this.offer.isNew) {
            this._saveOfferProperty('code', slugify(e.target.value));
        }
    }

    @action
    setPortalTitle(e) {
        this.isDisplayTitleEdited = true;
        this._saveOfferProperty('displayTitle', e.target.value);
    }

    @action
    setPortalDescription(e) {
        this._saveOfferProperty('displayDescription', e.target.value);
    }

    @action
    setOfferCode(e) {
        this.isOfferCodeEdited = true;
        this._saveOfferProperty('code', e.target.value);
    }

    @action
    setDurationInMonths(e) {
        this._saveOfferProperty('durationInMonths', e.target.value);
    }

    @action
    openConfirmArchiveModal() {
        if (!this.offer.isNew) {
            this.modals.open(ArchiveOfferModal, {
                offer: this.offer
            });
        }
    }

    @action
    openConfirmUnarchiveModal() {
        if (!this.offer.isNew) {
            this.modals.open(UnarchiveOfferModal, {
                offer: this.offer
            });
        }
    }

    get offerUrl() {
        const code = this.offer?.code || '';
        if (code) {
            const siteUrl = this.config.get('blogUrl');
            return `${siteUrl}/${slugify(code)}`;
        }
        return '';
    }

    get displayCurrency() {
        const tierId = this.offer?.tier?.id;
        if (!tierId) {
            return '$';
        }
        const tier = this.tiers.findBy('id', tierId);
        const tierCurrency = tier?.currency || 'usd';
        return getSymbol(tierCurrency);
    }

    get currencyLength() {
        return this.displayCurrency.length;
    }

    @action
    updateDurations(cadence) {
        if (cadence) {
            if (cadence === 'month') {
                this.durations = [
                    {
                        label: 'First-payment',
                        duration: 'once'
                    },
                    {
                        label: 'Multiple-months',
                        duration: 'repeating'
                    },
                    {
                        label: 'Forever',
                        duration: 'forever'
                    }
                ];
            } else {
                this.durations = [
                    {
                        label: 'First-payment',
                        duration: 'once'
                    },
                    {
                        label: 'Forever',
                        duration: 'forever'
                    }
                ];
                if (this.offer.duration === 'repeating') {
                    this._saveOfferProperty('duration', 'once');
                }
            }
        }
    }

    @action
    changeType(type) {
        if (type === 'trial') {
            this._saveOfferProperty('type', 'trial');
            this._saveOfferProperty('amount', 7);
            this._saveOfferProperty('duration', 'trial');
        } else {
            this._saveOfferProperty('type', 'percent');
            this._saveOfferProperty('amount', 0);
            this._saveOfferProperty('duration', 'once');
        }
    }

    @action
    updateCadence(cadence, offerObj) {
        offerObj = offerObj || this.offer;
        if (cadence) {
            const [tierId, tierCadence, currency] = cadence.split('-');
            offerObj.tier = {
                id: tierId
            };
            offerObj.cadence = tierCadence;
            offerObj.currency = currency;
            this.offertypes = [
                {
                    label: '%',
                    offertype: 'percent'
                },
                {
                    label: currency.toUpperCase(),
                    offertype: 'fixed'
                }
            ];
            this.updateDurations(tierCadence);
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @action
    updateDuration(duration) {
        this._saveOfferProperty('duration', duration);
    }

    // Private -----------------------------------------------------------------

    _saveOfferProperty(propKey, newValue) {
        let currentValue = this.offer[propKey];

        // avoid modifying empty values and triggering inadvertant unsaved changes modals
        if (newValue !== false && !newValue && !currentValue) {
            return;
        }

        this.offer[propKey] = newValue;
        this.updatePortalPreview({forceRefresh: false});
    }
}
<commit_msg>Removed pricing from the select field values<commit_after>import ArchiveOfferModal from '../components/modals/offers/archive';
import Controller, {inject as controller} from '@ember/controller';
import UnarchiveOfferModal from '../components/modals/offers/unarchive';
import config from 'ghost-admin/config/environment';
import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard';
import {action} from '@ember/object';
import {getSymbol} from 'ghost-admin/utils/currency';
import {ghPriceAmount} from '../helpers/gh-price-amount';
import {inject as service} from '@ember/service';
import {slugify} from '@tryghost/string';
import {task} from 'ember-concurrency';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

export default class OffersController extends Controller {
    @controller offers;
    @service config;
    @service settings;
    @service store;
    @service modals;
    @service feature;
    @service membersUtils;
    @service notifications;

    @tracked cadences = [];
    @tracked tiers = [];
    @tracked portalPreviewUrl = '';
    @tracked showUnsavedChangesModal = false;

    @tracked defaultSiteUrl = this.config.get('blogUrl');

    @tracked durations = [
        {
            label: 'Forever',
            duration: 'forever'
        },
        {
            label: 'First-payment',
            duration: 'once'
        },
        {
            label: 'Multiple-months',
            duration: 'repeating'
        }
    ];

    @tracked offertypes = [
        {
            label: '%',
            offertype: 'percent'
        },
        {
            label: 'USD',
            offertype: 'fixed'
        }
    ];

    @tracked defaultProps = null;
    @tracked isDisplayTitleEdited = false;
    @tracked isOfferCodeEdited = false;

    leaveScreenTransition = null;
    portalPreviewGuid = Date.now().valueOf();

    constructor() {
        super(...arguments);
        if (this.isTesting === undefined) {
            this.isTesting = config.environment === 'test';
        }
    }

    get offer() {
        return this.model;
    }

    set offer(offer) {
        this.model = offer;
    }

    get scratchOffer() {
        return {
            ...this.offer
        };
    }

    get isTrialOffer() {
        return this.offer?.type === 'trial';
    }

    get isDiscountOffer() {
        return this.offer?.type !== 'trial';
    }

    get cadence() {
        if (this.offer.tier && this.offer.cadence) {
            const tier = this.tiers.findBy('id', this.offer.tier.id);
            return `${this.offer.tier.id}-${this.offer.cadence}-${tier?.currency}`;
        } else if (this.defaultProps) {
            const tier = this.tiers.findBy('id', this.defaultProps.tier.id);
            return `${this.defaultProps.tier.id}-${this.defaultProps.cadence}-${tier?.currency}`;
        }
        return '';
    }

    get isDiscountSectionDisabled() {
        return !this.offer.isNew;
    }

    // Tasks -------------------------------------------------------------------

    @task({drop: true})
    *fetchTiers() {
        this.tiers = yield this.store.query('tier', {filter: 'type:paid+active:true', include: 'monthly_price,yearly_price'});
        this.tiers = this.tiers.filter((d) => {
            return d.monthlyPrice && d.yearlyPrice;
        });
        const cadences = [];
        this.tiers.forEach((tier) => {
            let monthlyLabel;
            let yearlyLabel;
            const tierCurrency = tier.currency;
            const tierCurrencySymbol = tierCurrency.toUpperCase();
            monthlyLabel = `${tier.name} - Monthly`;
            yearlyLabel = `${tier.name} - Yearly`;

            cadences.push({
                label: monthlyLabel,
                name: `${tier.id}-month-${tierCurrency}`
            });

            cadences.push({
                label: yearlyLabel,
                name: `${tier.id}-year-${tierCurrency}`
            });
        });
        this.cadences = cadences;
        const defaultCadence = this.cadences[0]?.name;
        const [,interval, defaultCurrency] = (defaultCadence || '').split('-');

        this.updateDurations(interval);
        if (this.offer && !this.offer.tier) {
            this.defaultProps = {};
            this.updateCadence(defaultCadence, this.defaultProps);
            this.updatePortalPreview({forceRefresh: false});
        } else if (defaultCadence) {
            this.offertypes = [
                {
                    label: '%',
                    offertype: 'percent'
                },
                {
                    label: defaultCurrency.toUpperCase(),
                    offertype: 'fixed'
                }
            ];
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @task({drop: true})
    *copyOfferUrl() {
        copyTextToClipboard(this.offerUrl);
        yield timeout(this.isTesting ? 50 : 500);
        return true;
    }

    @task({drop: true})
    *saveTask() {
        let {offer} = this;

        if (!offer.tier && this.defaultProps) {
            this.offer.tier = {
                id: this.defaultProps?.tier.id
            };
            this.offer.cadence = this.defaultProps.cadence;
            this.offer.currency = this.defaultProps.currency;
        }

        try {
            yield this.offer.validate();
            yield offer.save();

            // replace 'offer.new' route with 'offer' route
            this.replaceRoute('offer', offer);

            return offer;
        } catch (error) {
            if (error) {
                this.notifications.showAPIError(error, {key: 'offer.save'});
            }
        }
    }

    @task
    *fetchOfferTask(offerId) {
        this.isLoading = true;

        this.offer = yield this.store.queryRecord('offer', {
            id: offerId
        });

        this.isLoading = false;
    }

    @action
    portalPreviewInserted(iframe) {
        this.portalPreviewIframe = iframe;

        if (!this.portalMessageListener) {
            this.portalMessageListener = (event) => {
                const resizeEvents = ['portal-ready', 'portal-preview-updated'];
                if (resizeEvents.includes(event.data.type) && event.data.payload?.height && this.portalPreviewIframe?.parentNode) {
                    this.portalPreviewIframe.parentNode.style.height = `${event.data.payload.height}px`;
                }
            };

            window.addEventListener('message', this.portalMessageListener, true);
        }
    }

    @action
    portalPreviewDestroyed() {
        this.portalPreviewIframe = null;

        if (this.portalMessageListener) {
            window.removeEventListener('message', this.portalMessageListener);
        }
    }

    @action
    updatePortalPreview({forceRefresh} = {forceRefresh: false}) {
        const newUrl = new URL(this.membersUtils.getOfferPortalPreviewUrl({
            name: this.offer.name || 'No Name',
            code: this.offer.code || 'no-code',
            displayTitle: this.offer.displayTitle,
            displayDescription: this.offer.displayDescription,
            type: this.offer.type,
            cadence: this.offer.cadence || this.defaultProps?.cadence,
            amount: this.offer.amount,
            duration: this.offer.duration,
            durationInMonths: this.offer.durationInMonths,
            currency: this.offer.currency || this.defaultProps?.currency,
            status: this.offer.currency,
            tierId: this.offer?.tier?.id || this.defaultProps?.tier?.id
        }));

        if (forceRefresh) {
            this.portalPreviewGuid = Date.now().valueOf();
        }
        newUrl.searchParams.set('v', `${this.portalPreviewGuid}`);

        this.portalPreviewUrl = newUrl;
    }

    @action
    save() {
        return this.saveTask.perform();
    }

    @action
    leaveScreen() {
        this.offer.rollbackAttributes();
        return this.leaveScreenTransition.retry();
    }

    @action
    toggleUnsavedChangesModal(transition) {
        let leaveTransition = this.leaveScreenTransition;

        if (!transition && this.showUnsavedChangesModal) {
            this.leaveScreenTransition = null;
            this.showUnsavedChangesModal = false;
            return;
        }

        if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
            this.leaveScreenTransition = transition;

            // if a save is running, wait for it to finish then transition
            if (this.save.isRunning) {
                return this.save.last.then(() => {
                    transition.retry();
                });
            }

            // we genuinely have unsaved data, show the modal
            this.showUnsavedChangesModal = true;
        }
    }

    @action
    setup() {
        this.fetchTiers.perform();
    }

    @action
    setProperty(propKey, value) {
        this._saveOfferProperty(propKey, value);
    }

    @action
    setDiscountType(discountType) {
        if (!this.isDiscountSectionDisabled) {
            const amount = this.offer.amount || 0;
            this._saveOfferProperty('type', discountType);
            if (this.offer.type === 'fixed' && this.offer.amount !== '') {
                this.offer.amount = amount * 100;
            } else if (this.offer.amount !== '') {
                this.offer.amount = amount / 100;
            }
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @action
    setDiscountAmount(e) {
        let amount = e.target.value;
        if (this.offer.type === 'fixed' && amount !== '') {
            amount = parseFloat(amount) * 100;
        }
        this._saveOfferProperty('amount', amount);
    }

    @action
    setTrialDuration(e) {
        let amount = e.target.value;
        if (amount !== '') {
            amount = parseInt(amount);
        }
        this._saveOfferProperty('amount', amount);
    }

    @action
    setOfferName(e) {
        this._saveOfferProperty('name', e.target.value);
        if (!this.isDisplayTitleEdited && this.offer.isNew) {
            this._saveOfferProperty('displayTitle', e.target.value);
        }

        if (!this.isOfferCodeEdited && this.offer.isNew) {
            this._saveOfferProperty('code', slugify(e.target.value));
        }
    }

    @action
    setPortalTitle(e) {
        this.isDisplayTitleEdited = true;
        this._saveOfferProperty('displayTitle', e.target.value);
    }

    @action
    setPortalDescription(e) {
        this._saveOfferProperty('displayDescription', e.target.value);
    }

    @action
    setOfferCode(e) {
        this.isOfferCodeEdited = true;
        this._saveOfferProperty('code', e.target.value);
    }

    @action
    setDurationInMonths(e) {
        this._saveOfferProperty('durationInMonths', e.target.value);
    }

    @action
    openConfirmArchiveModal() {
        if (!this.offer.isNew) {
            this.modals.open(ArchiveOfferModal, {
                offer: this.offer
            });
        }
    }

    @action
    openConfirmUnarchiveModal() {
        if (!this.offer.isNew) {
            this.modals.open(UnarchiveOfferModal, {
                offer: this.offer
            });
        }
    }

    get offerUrl() {
        const code = this.offer?.code || '';
        if (code) {
            const siteUrl = this.config.get('blogUrl');
            return `${siteUrl}/${slugify(code)}`;
        }
        return '';
    }

    get displayCurrency() {
        const tierId = this.offer?.tier?.id;
        if (!tierId) {
            return '$';
        }
        const tier = this.tiers.findBy('id', tierId);
        const tierCurrency = tier?.currency || 'usd';
        return getSymbol(tierCurrency);
    }

    get currencyLength() {
        return this.displayCurrency.length;
    }

    @action
    updateDurations(cadence) {
        if (cadence) {
            if (cadence === 'month') {
                this.durations = [
                    {
                        label: 'First-payment',
                        duration: 'once'
                    },
                    {
                        label: 'Multiple-months',
                        duration: 'repeating'
                    },
                    {
                        label: 'Forever',
                        duration: 'forever'
                    }
                ];
            } else {
                this.durations = [
                    {
                        label: 'First-payment',
                        duration: 'once'
                    },
                    {
                        label: 'Forever',
                        duration: 'forever'
                    }
                ];
                if (this.offer.duration === 'repeating') {
                    this._saveOfferProperty('duration', 'once');
                }
            }
        }
    }

    @action
    changeType(type) {
        if (type === 'trial') {
            this._saveOfferProperty('type', 'trial');
            this._saveOfferProperty('amount', 7);
            this._saveOfferProperty('duration', 'trial');
        } else {
            this._saveOfferProperty('type', 'percent');
            this._saveOfferProperty('amount', 0);
            this._saveOfferProperty('duration', 'once');
        }
    }

    @action
    updateCadence(cadence, offerObj) {
        offerObj = offerObj || this.offer;
        if (cadence) {
            const [tierId, tierCadence, currency] = cadence.split('-');
            offerObj.tier = {
                id: tierId
            };
            offerObj.cadence = tierCadence;
            offerObj.currency = currency;
            this.offertypes = [
                {
                    label: '%',
                    offertype: 'percent'
                },
                {
                    label: currency.toUpperCase(),
                    offertype: 'fixed'
                }
            ];
            this.updateDurations(tierCadence);
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @action
    updateDuration(duration) {
        this._saveOfferProperty('duration', duration);
    }

    // Private -----------------------------------------------------------------

    _saveOfferProperty(propKey, newValue) {
        let currentValue = this.offer[propKey];

        // avoid modifying empty values and triggering inadvertant unsaved changes modals
        if (newValue !== false && !newValue && !currentValue) {
            return;
        }

        this.offer[propKey] = newValue;
        this.updatePortalPreview({forceRefresh: false});
    }
}
<|endoftext|>


<commit_before>module.exports = function(config) {
    config.set({

        basePath: __dirname + "/..",
        frameworks: ["jasmine"],
        //plugins:["karma-ng-html2js-preprocessor"],
        // List of files to load in the browser
        files: [
            "https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js",
            "http://maps.googleapis.com/maps/api/js?sensor=false&language=en",
            "node_modules/angular/angular.js",
            "node_modules/angular-animate/angular-animate.js",
            "node_modules/angular-aria/angular-aria.js",
            "node_modules/angular-material/angular-material.js",
            "node_modules/angular-material/angular-material-mocks.js",
            "node_modules/angular-mocks/angular-mocks.js",
            "public/js/**/*.js",
            "spec/mocks/googleMapsApi.js",
            "spec/frontend/**/*.js",
            "public/templates/*.html"
        ],

        exclude: [],
        preprocessors: {
            // Location of templates
            "public/templates/*.html": ["ng-html2js"],
            "public/js/**/*.js": ["coverage"]
        },
        ngHtml2JsPreprocessor: {
            stripPrefix: "public",
            // The name of the Angular module to create
            moduleName: "templates"
        },
        coverageReporter: {
            type: "html",
            dir: "coverage",
            watermarks: {
                statements: [ 50, 75 ],
                functions: [ 50, 75 ],
                branches: [ 50, 75 ],
                lines: [ 50, 75 ]
            }
        },
        reporters: ["progress", "coverage"],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: false,
        browsers: ["Chrome"],
        singleRun: true,

        // How many browser should be started simultaneous
        concurrency: Infinity
    })
}
<commit_msg>Added coverage threshold: global and individual<commit_after>module.exports = function(config) {
    config.set({

        basePath: __dirname + "/..",
        frameworks: ["jasmine"],
        //plugins:["karma-ng-html2js-preprocessor"],
        // List of files to load in the browser
        files: [
            "https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js",
            "node_modules/angular/angular.js",
            "node_modules/angular-animate/angular-animate.js",
            "node_modules/angular-aria/angular-aria.js",
            "node_modules/angular-material/angular-material.js",
            "node_modules/angular-material/angular-material-mocks.js",
            "node_modules/angular-mocks/angular-mocks.js",
            "public/js/**/*.js",
            "spec/mocks/googleMapsApi.js",
            "spec/frontend/**/*.js",
            "public/templates/*.html"
        ],

        exclude: [],
        preprocessors: {
            // Location of templates
            "public/templates/*.html": ["ng-html2js"],
            "public/js/**/*.js": ["coverage"]
        },
        ngHtml2JsPreprocessor: {
            stripPrefix: "public",
            // The name of the Angular module to create
            moduleName: "templates"
        },
        coverageReporter: {
            type: "html",
            dir: "coverage",
            check: {
                global: {
                    statements: 80,
                    branches: 80,
                    functions: 80,
                    lines: 80,
                    excludes: [ ]
                },
                each: {
                    statements: 50,
                    branches: 50,
                    functions: 50,
                    lines: 50,
                    excludes: [ ]
                }
            },
            watermarks: {
                statements: [ 50, 75 ],
                functions: [ 50, 75 ],
                branches: [ 50, 75 ],
                lines: [ 50, 75 ]
            }
        },
        reporters: ["progress", "coverage"],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: false,
        browsers: ["Chrome"],
        singleRun: true,

        // How many browser should be started simultaneous
        concurrency: Infinity
    })
}
<|endoftext|>


<commit_before>/*globals require*/
/*jshint browser: true*/
var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;

var pathToModule = function (path) {
    'use strict';
    return path.replace(/^\/base\//, '').replace(/\.js$/, '');
};

Object.keys(window.__karma__.files).forEach(function (file) {
    'use strict';
    if (TEST_REGEXP.test(file)) {
        // Normalize paths to RequireJS module names.
        allTestFiles.push(pathToModule(file));
    }
});

require.config({
    // Karma serves files under /base, which is the basePath from your config file
    baseUrl: '/base',

    map: {
        '*': {
            'css': './src/client/lib/require/require-css/css',
            'text': './src/client/lib/require/require-text/text'
        }
    },

    paths: {
        // plugin base classes
        'plugin': './src/plugin',

        // plugins
        // TODO: populate plugin list dynamically based on config.json

        // MAGIC ... from src/client/js/main.js
        'executor': './src/common/executor',
        'blob': './src/common/blob',
        'common': './src/common',
        //'core': './src/common/core',
        //'storage': './src/common/storage',

        'js': './src/client/js',
        //'util': './src/common/util',
        //'eventDispatcher': './src/common/EventDispatcher',
        //'logManager': './src/common/LogManager',
        //'coreclient': './src/common/core/users',

        'superagent': './src/client/lib/superagent/superagent-1.1.0',
        'jszip': './src/client/lib/jszip/jszip',
        'debug': './src/client/lib/debug/debug',
        'underscore': './src/client/lib/underscore/underscore',

        'karmatest': './test-karma',
        'aRtestCases': './test-karma/client/js/AutoRouter/testCases'

        // external libraries used by plugins
        //'ejs': './support/ejs/ejs.min',
        //'xmljsonconverter': './lib/xmljsonconverter',
        //'sax': './support/sax/sax',

        // modules used by test cases
        //'mocks': './test/mocks',
        //'models': './test/models'
    },

    // dynamically load all test files
    deps: allTestFiles,

    // we have to kickoff jasmine, as it is asynchronous
    callback: window.__karma__.start
});
<commit_msg>#370 Add Q to karma main config.<commit_after>/*globals require*/
/*jshint browser: true*/
var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;

var pathToModule = function (path) {
    'use strict';
    return path.replace(/^\/base\//, '').replace(/\.js$/, '');
};

Object.keys(window.__karma__.files).forEach(function (file) {
    'use strict';
    if (TEST_REGEXP.test(file)) {
        // Normalize paths to RequireJS module names.
        allTestFiles.push(pathToModule(file));
    }
});

require.config({
    // Karma serves files under /base, which is the basePath from your config file
    baseUrl: '/base',

    map: {
        '*': {
            'css': './src/client/lib/require/require-css/css',
            'text': './src/client/lib/require/require-text/text'
        }
    },

    paths: {
        // plugin base classes
        'plugin': './src/plugin',

        // plugins
        // TODO: populate plugin list dynamically based on config.json

        // MAGIC ... from src/client/js/main.js
        'executor': './src/common/executor',
        'blob': './src/common/blob',
        'common': './src/common',
        //'core': './src/common/core',
        //'storage': './src/common/storage',

        'js': './src/client/js',
        //'util': './src/common/util',
        //'eventDispatcher': './src/common/EventDispatcher',
        //'logManager': './src/common/LogManager',
        //'coreclient': './src/common/core/users',

        'superagent': './src/client/lib/superagent/superagent-1.1.0',
        'jszip': './src/client/lib/jszip/jszip',
        'debug': './src/client/lib/debug/debug',
        'underscore': './src/client/lib/underscore/underscore',
        'Q': './src/client/lib/q/q',

        'karmatest': './test-karma',
        'aRtestCases': './test-karma/client/js/AutoRouter/testCases'

        // external libraries used by plugins
        //'ejs': './support/ejs/ejs.min',
        //'xmljsonconverter': './lib/xmljsonconverter',
        //'sax': './support/sax/sax',

        // modules used by test cases
        //'mocks': './test/mocks',
        //'models': './test/models'
    },

    // dynamically load all test files
    deps: allTestFiles,

    // we have to kickoff jasmine, as it is asynchronous
    callback: window.__karma__.start
});
<|endoftext|>


<commit_before>import eeprom from '../utils/eeprom';

import {
  ALARM, ALARM_ENABLED, ALARM_VALUE, AS_BASE_STATE, BAUD_RATE, CHANNEL,
  FC_TYPE, FONT_SIZE, H_ALIGNMENT, MAX, MAX_PANELS, MIN, OFFSET, PANEL,
  PARAMS_FROM_EEPROM, POSITION, RADIUS, RAW, SCALE, SCALE_ALIGNMENT,
  SCALE_ENABLED, SCALE_TYPE, TYPE, UNITS, V_ALIGNMENT, VALUE, MODE,
  VISIBLE_ON,
} from './actions';

function addPreviewState(state) {
  return state
    .updateIn(['preview', 'panel'], () => 0)
    .updateIn(['preview', 'alarm'], () => 0);
}

function setAsBaseState(state, baseState = null) {
  return state.updateIn(['originalState'], () => baseState || state);
}

const initialState = setAsBaseState(addPreviewState(eeprom.toParameters(eeprom.defaultEEPROM)));

export default function parameters(state = initialState, action) {
  const parameterName = action.parameter;
  const payload = action.payload;

  switch (action.type) {
    case ALARM:
      return state.setIn([parameterName, 'alarm'], payload);
    case ALARM_ENABLED:
      return state.setIn([parameterName, `${payload.alarm}Enabled`], payload.enabled);
    case ALARM_VALUE:
      return state.setIn([parameterName, `${payload.alarm}Value`], payload.value);
    case AS_BASE_STATE:
      return setAsBaseState(state, action.state);
    case BAUD_RATE:
      return state.setIn([parameterName, 'baudRate'], payload);
    case CHANNEL:
      return state.setIn([parameterName, `${payload.key}Channel`], payload.channel);
    case FC_TYPE:
      return state.updateIn([parameterName, 'fcType'], () => action.fcType);
    case FONT_SIZE:
      return state.setIn([parameterName, 'fontSize'], payload);
    case H_ALIGNMENT:
      return state.setIn([parameterName, 'hAlignment'], payload);
    case MAX:
      return state.setIn([parameterName, 'max'], payload);
    case MAX_PANELS:
      return state.setIn([parameterName, 'maxPanels'], payload);
    case MIN:
      return state.setIn([parameterName, 'min'], payload);
    case OFFSET:
      return state
        .setIn([parameterName, 'offsetX'], payload.x)
        .setIn([parameterName, 'offsetY'], payload.y);
    case PANEL:
      return state.updateIn([parameterName, 'panel'], () => action.panel);
    case PARAMS_FROM_EEPROM:
      return addPreviewState(setAsBaseState(eeprom.toParameters(action.eepromData)));
    case POSITION:
      return state
        .setIn([parameterName, 'positionX'], payload.x)
        .setIn([parameterName, 'positionY'], payload.y);
    case RADIUS:
      return state.updateIn([parameterName, action.key], () => action.radius);
    case RAW:
      return state.updateIn([parameterName, 'raw'], () => action.raw);
    case SCALE:
      return state.updateIn([parameterName, 'scale'], () => action.scale);
    case SCALE_ALIGNMENT:
      return state.updateIn([parameterName, 'scaleAlignment'], () => action.scaleAlignment);
    case SCALE_ENABLED:
      return state.updateIn([parameterName, 'scaleEnabled'], () => action.scaleEnabled);
    case SCALE_TYPE:
      return state.updateIn([parameterName, 'scaleType'], () => action.scaleType);
    case TYPE:
      return state.updateIn([parameterName, 'type'], () => action.typeValue);
    case UNITS:
      return state.updateIn([parameterName, 'units'], () => action.units);
    case V_ALIGNMENT:
      return state.updateIn([parameterName, 'vAlignment'], () => action.vAlignment);
    case VALUE:
      return state.updateIn([parameterName, `${action.key}Value`], () => action.value);
    case MODE:
      return state.updateIn([parameterName, `${action.prefix}Mode`], () => action.mode);
    case VISIBLE_ON:
      return state.setIn([parameterName, 'visibleOn'], payload);
    default:
      return state;
  }
}
<commit_msg>fix(config): reading saved config files<commit_after>import eeprom from '../utils/eeprom';

import {
  ALARM, ALARM_ENABLED, ALARM_VALUE, AS_BASE_STATE, BAUD_RATE, CHANNEL,
  FC_TYPE, FONT_SIZE, H_ALIGNMENT, MAX, MAX_PANELS, MIN, OFFSET, PANEL,
  PARAMS_FROM_EEPROM, POSITION, RADIUS, RAW, SCALE, SCALE_ALIGNMENT,
  SCALE_ENABLED, SCALE_TYPE, TYPE, UNITS, V_ALIGNMENT, VALUE, MODE,
  VISIBLE_ON,
} from './actions';

function addPreviewState(state) {
  return state
    .updateIn(['preview', 'panel'], () => 0)
    .updateIn(['preview', 'alarm'], () => 0);
}

function setAsBaseState(state, baseState = null) {
  return state.updateIn(['originalState'], () => baseState || state);
}

const initialState = setAsBaseState(addPreviewState(eeprom.toParameters(eeprom.defaultEEPROM)));

export default function parameters(state = initialState, action) {
  const parameterName = action.parameter;
  const payload = action.payload;

  switch (action.type) {
    case ALARM:
      return state.setIn([parameterName, 'alarm'], payload);
    case ALARM_ENABLED:
      return state.setIn([parameterName, `${payload.alarm}Enabled`], payload.enabled);
    case ALARM_VALUE:
      return state.setIn([parameterName, `${payload.alarm}Value`], payload.value);
    case AS_BASE_STATE:
      return setAsBaseState(state, action.state);
    case BAUD_RATE:
      return state.setIn([parameterName, 'baudRate'], payload);
    case CHANNEL:
      return state.setIn([parameterName, `${payload.key}Channel`], payload.channel);
    case FC_TYPE:
      return state.updateIn([parameterName, 'fcType'], () => action.fcType);
    case FONT_SIZE:
      return state.setIn([parameterName, 'fontSize'], payload);
    case H_ALIGNMENT:
      return state.setIn([parameterName, 'hAlignment'], payload);
    case MAX:
      return state.setIn([parameterName, 'max'], payload);
    case MAX_PANELS:
      return state.setIn([parameterName, 'maxPanels'], payload);
    case MIN:
      return state.setIn([parameterName, 'min'], payload);
    case OFFSET:
      return state
        .setIn([parameterName, 'offsetX'], payload.x)
        .setIn([parameterName, 'offsetY'], payload.y);
    case PANEL:
      return state.updateIn([parameterName, 'panel'], () => action.panel);
    case PARAMS_FROM_EEPROM:
      return setAsBaseState(addPreviewState(eeprom.toParameters(action.eepromData)));
    case POSITION:
      return state
        .setIn([parameterName, 'positionX'], payload.x)
        .setIn([parameterName, 'positionY'], payload.y);
    case RADIUS:
      return state.updateIn([parameterName, action.key], () => action.radius);
    case RAW:
      return state.updateIn([parameterName, 'raw'], () => action.raw);
    case SCALE:
      return state.updateIn([parameterName, 'scale'], () => action.scale);
    case SCALE_ALIGNMENT:
      return state.updateIn([parameterName, 'scaleAlignment'], () => action.scaleAlignment);
    case SCALE_ENABLED:
      return state.updateIn([parameterName, 'scaleEnabled'], () => action.scaleEnabled);
    case SCALE_TYPE:
      return state.updateIn([parameterName, 'scaleType'], () => action.scaleType);
    case TYPE:
      return state.updateIn([parameterName, 'type'], () => action.typeValue);
    case UNITS:
      return state.updateIn([parameterName, 'units'], () => action.units);
    case V_ALIGNMENT:
      return state.updateIn([parameterName, 'vAlignment'], () => action.vAlignment);
    case VALUE:
      return state.updateIn([parameterName, `${action.key}Value`], () => action.value);
    case MODE:
      return state.updateIn([parameterName, `${action.prefix}Mode`], () => action.mode);
    case VISIBLE_ON:
      return state.setIn([parameterName, 'visibleOn'], payload);
    default:
      return state;
  }
}
<|endoftext|>


<commit_before>var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/User');

module.exports = function(passport) {
  passport.serializeUser(function(user, done) {
    done(null, user.id);
  });

  passport.deserializeUser(function(id, done) {
    User.findById(id, function(err, user) {
        done(err, user);
    });
  });

  passport.use('local', new LocalStrategy(
    function(username, password, done) {
      User.validatePassword(username, password, done);
    }));

  passport.use('local-signup', new LocalStrategy(
    function(username, password, done) {
      User.findOne(username, done, function(err, user) {
        if (err) return done(err);
        if (user) return done(null, false, "That username is already taken");
        else {
          User.create(username, password, 1, done);
        }
      });
    }));
};
<commit_msg>Changed require for user to see if it works on Heroku<commit_after>var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user.js');

module.exports = function(passport) {
  passport.serializeUser(function(user, done) {
    done(null, user.id);
  });

  passport.deserializeUser(function(id, done) {
    User.findById(id, function(err, user) {
        done(err, user);
    });
  });

  passport.use('local', new LocalStrategy(
    function(username, password, done) {
      User.validatePassword(username, password, done);
    }));

  passport.use('local-signup', new LocalStrategy(
    function(username, password, done) {
      User.findOne(username, done, function(err, user) {
        if (err) return done(err);
        if (user) return done(null, false, "That username is already taken");
        else {
          User.create(username, password, 1, done);
        }
      });
    }));
};
<|endoftext|>


<commit_before>'use strict'

const fs = require('fs');
const glob = require('glob');
const isBuiltInModule = require('is-builtin-module');
const syncExec = require("sync-exec");
const ora = require('ora');
const logSymbols = require('log-symbols');

/* Get installed modules
 * Read dependencies array from package.json
 */

let getInstalledModules = () => {
    let content = JSON.parse(readFile('package.json'));
    let installedModules = [];
    for (let key in content.dependencies) installedModules.push(key);
    return installedModules;
};

/* Get used modules
 * Read all .js files and grep for modules
 */

let getUsedModules = () => {
    let files = getFiles();
    let usedModules = [];
    for (let i = 0; i < files.length; i++) {
        let modulesFromFile = getModulesFromFile(files[i]);
        usedModules = usedModules.concat(modulesFromFile);
    }
    // De-duplicate
    usedModules = usedModules.filter((module, position) => {
        return usedModules.indexOf(module) === position;
    });
    return usedModules;
};

/* Install module
 * Install given module
 */

let installModule = (module) => {
    let spinner = startSpinner('Installing ' + module, 'green');
    let success = runCommand('npm install ' + module + ' --save');
    if (success) stopSpinner(spinner, module + ' installed', 'green');
    else stopSpinner(spinner, module + ' installation failed', 'yellow');
};

/* Uninstall module */

let uninstallModule = (module) => {
    let spinner = startSpinner('Uninstalling ' + module, 'red');
    runCommand('npm uninstall ' + module + ' --save');
    stopSpinner(spinner, module + ' removed', 'red');
};

/* Command runner
 * Run a given command
 */

let runCommand = (command) => {
    let response = syncExec(command);
    return !response.status; // status = 0 for success
};

/* Show pretty outputs
 * Use ora spinners to show what's going on
 */

let startSpinner = (message, type) => {
    let spinner = ora();
    spinner.text = message;
    spinner.color = type;
    spinner.start();
    return spinner;
};

let stopSpinner = (spinner, message, type) => {
    spinner.stop();
    if (!message) return;
    let symbol;
    if (type === 'red') symbol = logSymbols.error;
    else if (type === 'yellow') symbol = logSymbols.warning;
    else symbol = logSymbols.success;
    console.log(symbol, message);
};

/* Get all js files
 * Return path of all js files
 */
let getFiles = (path) => {
    return glob.sync("**/*.js", {'ignore': ['node_modules/**/*']});
};

/* File reader
 * Return contents of given file
 */
let readFile = (path) => {
    let content = fs.readFileSync(path, 'utf8');
    return content;
};

/* Find modules from file
 * Returns array of modules from a file
 */

let pattern = /require\((.*?)\)/g;

let getModulesFromFile = (path) => {
    let content = fs.readFileSync(path, 'utf8');
    let modules = [];
    let matches = content.match(pattern);
    if (!matches) return modules;
    for(let i = 0; i < matches.length; i++) {
        let match = matches[i];
        match = match.replace('require', '');
        match = match.substring(2)
        match = match.substring(0, match.length - 2);
        modules.push(match);
    }
    return modules;
};

/* Filter registry modules */

let filterRegistryModules = (modules) => {
    modules = removeBuiltInModules(modules);
    modules = removeLocalFiles(modules);
    return modules;
};

/* Remove built in/native modules */

let removeBuiltInModules = (modules) => {
    modules = modules.filter((module) => {
        return !isBuiltInModule(module);
    });
    return modules;
};

/* Remove local files that are required */

let removeLocalFiles = (modules) => {
    modules = modules.filter((module) => {
        return (module.indexOf('./') !== 0)
    });
    return modules;
};

/* Array diff prototype */

Array.prototype.diff = function(a) {
    return this.filter(function(i) {return a.indexOf(i) < 0;});
};

let reinstall = () => {
    let spinner = startSpinner('Cleaning up', 'green');
    runCommand('npm install');
    stopSpinner(spinner);
};

/* Public helper functions */

module.exports = {
    getInstalledModules,
    getUsedModules,
    filterRegistryModules,
    installModule,
    uninstallModule,
    reinstall
};

<commit_msg>Running validation on module names<commit_after>'use strict'

const fs = require('fs');
const glob = require('glob');
const isBuiltInModule = require('is-builtin-module');
const syncExec = require("sync-exec");
const ora = require('ora');
const logSymbols = require('log-symbols');

/* Get installed modules
 * Read dependencies array from package.json
 */

let getInstalledModules = () => {
    let content = JSON.parse(readFile('package.json'));
    let installedModules = [];
    for (let key in content.dependencies) installedModules.push(key);
    return installedModules;
};

/* Get used modules
 * Read all .js files and grep for modules
 */

let getUsedModules = () => {
    let files = getFiles();
    let usedModules = [];
    for (let i = 0; i < files.length; i++) {
        let modulesFromFile = getModulesFromFile(files[i]);
        usedModules = usedModules.concat(modulesFromFile);
    }
    // De-duplicate
    usedModules = usedModules.filter((module, position) => {
        return usedModules.indexOf(module) === position;
    });
    return usedModules;
};

/* Install module
 * Install given module
 */

let installModule = (module) => {
    let spinner = startSpinner('Installing ' + module, 'green');
    let success = runCommand('npm install ' + module + ' --save');
    if (success) stopSpinner(spinner, module + ' installed', 'green');
    else stopSpinner(spinner, module + ' installation failed', 'yellow');
};

/* Uninstall module */

let uninstallModule = (module) => {
    let spinner = startSpinner('Uninstalling ' + module, 'red');
    runCommand('npm uninstall ' + module + ' --save');
    stopSpinner(spinner, module + ' removed', 'red');
};

/* Command runner
 * Run a given command
 */

let runCommand = (command) => {
    let response = syncExec(command);
    return !response.status; // status = 0 for success
};

/* Show pretty outputs
 * Use ora spinners to show what's going on
 */

let startSpinner = (message, type) => {
    let spinner = ora();
    spinner.text = message;
    spinner.color = type;
    spinner.start();
    return spinner;
};

let stopSpinner = (spinner, message, type) => {
    spinner.stop();
    if (!message) return;
    let symbol;
    if (type === 'red') symbol = logSymbols.error;
    else if (type === 'yellow') symbol = logSymbols.warning;
    else symbol = logSymbols.success;
    console.log(symbol, message);
};

/* Get all js files
 * Return path of all js files
 */
let getFiles = (path) => {
    return glob.sync("**/*.js", {'ignore': ['node_modules/**/*']});
};

/* File reader
 * Return contents of given file
 */
let readFile = (path) => {
    let content = fs.readFileSync(path, 'utf8');
    return content;
};

/* Find modules from file
 * Returns array of modules from a file
 */

let pattern = /require\((.*?)\)/g;

let getModulesFromFile = (path) => {
    let content = fs.readFileSync(path, 'utf8');
    let modules = [];
    let matches = content.match(pattern);
    if (!matches) return modules;
    for(let i = 0; i < matches.length; i++) {
        let match = matches[i];
        match = match.replace('require', '');
        match = match.substring(2)
        match = match.substring(0, match.length - 2);
        if (isValidModule(match)) modules.push(match);
    }
    return modules;
};

/* Check for valid string - to stop malicious intentions */

let isValidModule = (module) => {
    let regex = new RegExp("^([a-z0-9-_]{1,})$");
    return regex.test(module);
};

/* Filter registry modules */

let filterRegistryModules = (modules) => {
    modules = removeBuiltInModules(modules);
    modules = removeLocalFiles(modules);
    return modules;
};

/* Remove built in/native modules */

let removeBuiltInModules = (modules) => {
    modules = modules.filter((module) => {
        return !isBuiltInModule(module);
    });
    return modules;
};

/* Remove local files that are required */

let removeLocalFiles = (modules) => {
    modules = modules.filter((module) => {
        return (module.indexOf('./') !== 0)
    });
    return modules;
};

/* Array diff prototype */

Array.prototype.diff = function(a) {
    return this.filter(function(i) {return a.indexOf(i) < 0;});
};

let reinstall = () => {
    let spinner = startSpinner('Cleaning up', 'green');
    runCommand('npm install');
    stopSpinner(spinner);
};

/* Public helper functions */

module.exports = {
    getInstalledModules,
    getUsedModules,
    filterRegistryModules,
    installModule,
    uninstallModule,
    reinstall
};

<|endoftext|>


<commit_before>import _ from 'lodash';

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, View } from 'react-native';

import Star from './Star'

export default class AirbnbRating extends Component {
  static defaultProps = {
    defaultRating: 5,
    reviews: ["Terrible", "Bad", "Okay", "Good", "Great"],
    count: 5,
    onFinishRating: () => console.log('Rating selected. Attach a function here.'),
    showRating: true
  };

  constructor() {
    super()

    this.state = {
      position: 5
    }
  }

  componentDidMount() {
    const { defaultRating } = this.props

    this.setState({ position: defaultRating })
  }

  renderStars(rating_array) {
    return _.map(rating_array, (star, index) => {
      return star
    })
  }

  starSelectedInPosition(position) {
    const { onFinishRating } = this.props

    onFinishRating(position);

    this.setState({ position: position })
  }

  render() {
    const { position } = this.state
    const { count, reviews, showRating } = this.props
    const rating_array = []

    _.times(count, index => {
      rating_array.push(
        <Star
          key={index}
          position={index + 1}
          starSelectedInPosition={this.starSelectedInPosition.bind(this)}
          fill={position >= index + 1}
          {...this.props}
        />
      )
    })

    return (
      <View style={styles.ratingContainer}>
        { showRating &&
          <Text style={styles.reviewText}>
            {reviews[position - 1]}
          </Text>
        }
        <View style={styles.starContainer}>
          {this.renderStars(rating_array)}
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  ratingContainer: {
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
  },
  reviewText: {
    fontSize: 25,
    fontWeight: 'bold',
    margin: 10,
    color: 'rgba(230, 196, 46, 1)'
  },
  starContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
  }
});
<commit_msg>fix(style): set transparent background<commit_after>import _ from 'lodash';

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, View } from 'react-native';

import Star from './Star'

export default class AirbnbRating extends Component {
  static defaultProps = {
    defaultRating: 5,
    reviews: ["Terrible", "Bad", "Okay", "Good", "Great"],
    count: 5,
    onFinishRating: () => console.log('Rating selected. Attach a function here.'),
    showRating: true
  };

  constructor() {
    super()

    this.state = {
      position: 5
    }
  }

  componentDidMount() {
    const { defaultRating } = this.props

    this.setState({ position: defaultRating })
  }

  renderStars(rating_array) {
    return _.map(rating_array, (star, index) => {
      return star
    })
  }

  starSelectedInPosition(position) {
    const { onFinishRating } = this.props

    onFinishRating(position);

    this.setState({ position: position })
  }

  render() {
    const { position } = this.state
    const { count, reviews, showRating } = this.props
    const rating_array = []

    _.times(count, index => {
      rating_array.push(
        <Star
          key={index}
          position={index + 1}
          starSelectedInPosition={this.starSelectedInPosition.bind(this)}
          fill={position >= index + 1}
          {...this.props}
        />
      )
    })

    return (
      <View style={styles.ratingContainer}>
        { showRating &&
          <Text style={styles.reviewText}>
            {reviews[position - 1]}
          </Text>
        }
        <View style={styles.starContainer}>
          {this.renderStars(rating_array)}
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  ratingContainer: {
    backgroundColor: 'transparent',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
  },
  reviewText: {
    fontSize: 25,
    fontWeight: 'bold',
    margin: 10,
    color: 'rgba(230, 196, 46, 1)'
  },
  starContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
  }
});
<|endoftext|>


<commit_before>'use strict'

import {Component}      from 'react'
import {Link}           from 'react-router'
import DocumentTitle    from 'react-document-title'

import MailchimpForm from '../components/MailchimpForm'

class TokenSalePage extends Component {

  constructor(props) {
    super(props)
  }

  render() {
    const subscribeURL = '//blockstack.us14.list-manage.com/subscribe/post?u=394a2b5cfee9c4b0f7525b009&id=0e5478ae86'

    return (
      <DocumentTitle title="Blockstack - Token Sale">

The Blockstack Token Sale

A new internet for decentralized apps

                        <MailchimpForm subscribeURL={subscribeURL} submitButtonText='Sign Up' />

                      <Link to="/whitepaper.pdf" target="_blank" className="btn btn-special btn-lg">
                        Read the Whitepaper
                      </Link>

      </DocumentTitle>
    )
  }

}

export default TokenSalePage
<commit_msg>token hero restructuring<commit_after>'use strict'

import {Component}         from 'react'
import {Link}              from 'react-router'
import DocumentTitle       from 'react-document-title'

import MailchimpForm       from '../components/MailchimpForm'
import TransparentHeader   from '../components/TransparentHeader'

class TokenSalePage extends Component {

  constructor(props) {
    super(props)
  }

  render() {
    const subscribeURL = '//blockstack.us14.list-manage.com/subscribe/post?u=394a2b5cfee9c4b0f7525b009&id=0e5478ae86'

    return (
      <DocumentTitle title="Blockstack - Token Sale">

            <TransparentHeader />

The Blockstack Token Sale

A new internet for decentralized apps

                    <MailchimpForm subscribeURL={subscribeURL} submitButtonText='Sign Up' />

                  <Link to="/whitepaper.pdf" target="_blank" className="btn btn-special btn-lg">
                    Read the Whitepaper
                  </Link>

      </DocumentTitle>
    )
  }

}

export default TokenSalePage
<|endoftext|>


<commit_before>/**
 * Map controller
 * Used to manage map layers and showing their related views
 */
Ext.define('CF.controller.Map', {
    extend: 'Ext.app.Controller',

    models: ['Summit'],
    stores: ['Summits'],

    refs: [
        {ref: 'summitChart', selector: 'summitchart'},
        {ref: 'summitGrid', selector: 'summitgrid'}
    ],

    // private properties

    summitTabPanel: null,

    mapPanel: null,

    init: function() {
        var me = this;

        me.getSummitsStore().on({
            scope: me,
            load : me.onSummitsStoreLoad
        });

        this.control({
            'cf_mappanel': {
                'beforerender': this.onMapPanelBeforeRender
            },
            'summittabpanel': {
                'beforerender': this.onSummitTabPanelBeforeRender
            },
            'summitgrid': {
                'beforeselect': this.onSummitGridBeforeSelect,
                'selectionchange': this.onSummitGridSelectionChange
            }
        }, this);
    },

    onMapPanelBeforeRender: function(mapPanel, options) {
        var me = this;

        var layers = [];

        // OpenLayers object creating
        var wms = new OpenLayers.Layer.WMS(
            "Hydro BNDT",
            "http://ows.geobase.ca/wms/geobase_fr",
            {layers: 'reference:hydro:hydro1m_p,reference:hydro:hydro1m_l'}
        );
        layers.push(wms);

		// OpenLayers object creating
        var wms = new OpenLayers.Layer.WMS(
            "Landsat BNDT",
            "http://ows.geobase.ca/wms/geobase_fr",
            {layers: 'l,imagery:landsat7:743'}
        );
        layers.push(wms);
		        // OpenLayers object creating
        var mapserv = new OpenLayers.Layer.WMS(
            "SIGEOM Geologie",
            "http://sigeom.mrn.gouv.qc.ca/SIGEOM_WMS/Request.aspx?",
            {layers: 'Geologie_detaillee_Quebec_50k',transparent:true}, {isBaseLayer: false}
        );
        layers.push(mapserv);
		        // OpenLayers object creating
        var mapserv = new OpenLayers.Layer.WMS(
            "SIGEOM Forage",
            "http://sigeom.mrn.gouv.qc.ca/SIGEOM_WMS/Request.aspx?",
            {layers: 'Forage_diamant',transparent:true}, {isBaseLayer: false}
        );
        layers.push(mapserv);

		// OpenLayers object creating
        var mapserv = new OpenLayers.Layer.WMS(
            "WMS MRB",
            "http://geohack2.mapgears.com/cgi-bin/mapserv?map=/opt/scribeui/application/workspaces/portail/demo/map/demo.map",
            {layers: 'default',transparent:true}, {isBaseLayer: false}
        );
        layers.push(mapserv);

        // create vector layer
        var context = {
            getColor: function(feature) {
                if (feature.attributes.elevation < 2000) {
                    return 'green';
                }
                if (feature.attributes.elevation < 2300) {
                    return 'orange';
                }
                return 'white';
            }
        };
        var template = {
            cursor: "pointer",
            fillOpacity: 0.01,
            fillColor: "${getColor}",
            pointRadius: 5,
            strokeWidth: 1,
            strokeOpacity: 0.01,
            strokeColor: "${getColor}",
            graphicName: "circle"
        };
        var style = new OpenLayers.Style(template, {context: context});
        var vecLayer = new OpenLayers.Layer.Vector("vector", {
            styleMap: new OpenLayers.StyleMap({
                  'default': style,
                  'select': {
            cursor: "pointer",
            fillOpacity: 0.7,
            fillColor: "#12FA29",
            pointRadius: 10,
            strokeWidth: 1,
            strokeOpacity: 1,
            strokeColor: "#12FA29",
            graphicName: "circle"}
            }),
            protocol: new OpenLayers.Protocol.HTTP({
                url: "data/Collar.json",
                format: new OpenLayers.Format.GeoJSON()
            }),
            strategies: [new OpenLayers.Strategy.Fixed()],
			displayInLayerSwitcher:false
        });
        layers.push(vecLayer);

        // manually bind store to layer
        me.getSummitsStore().bind(vecLayer);

        mapPanel.map.addLayers(layers);

        // for dev purpose
        map = mapPanel.map;
        mapPanel = mapPanel;

        // set private properties
        this.mapPanel = mapPanel;
    },

    onLaunch: function() {
        var me = this;

        // for dev purpose
        ctrl = this;
    },

    onSummitsStoreLoad: function(store, records) {
        // do custom stuff on summits load if you want, for example here we
        // zoom to summits extent
        var dataExtent = store.layer.getDataExtent();
        if (dataExtent) {
            store.layer.map.zoomToExtent(dataExtent);
        }
    },

    onSummitTabPanelBeforeRender: function(tabPanel, options) {
        this.summitTabPanel = tabPanel;
    },

    onSummitGridBeforeSelect: function(model, record, index, options) {
        var map = this.mapPanel.map,
            feature = record.raw,
            selectedFeatures = feature.layer.selectedFeatures;

        // feature about to be selected is no in the selected features array,
        // this means it was not selected using the map, but using the grid.
        // If so, recenter the map to feature location.
        if (Ext.Array.indexOf(selectedFeatures, feature) == -1) {
            map.setCenter(feature.geometry.getBounds().getCenterLonLat());
        }
    },

    onSummitGridSelectionChange: function(model, records) {
        if (records[0]) {
            this.summitTabPanel.loadRecord(records[0]);
        }
    }
});
<commit_msg>Fix GeoBase landsat WMS<commit_after>/**
 * Map controller
 * Used to manage map layers and showing their related views
 */
Ext.define('CF.controller.Map', {
    extend: 'Ext.app.Controller',

    models: ['Summit'],
    stores: ['Summits'],

    refs: [
        {ref: 'summitChart', selector: 'summitchart'},
        {ref: 'summitGrid', selector: 'summitgrid'}
    ],

    // private properties

    summitTabPanel: null,

    mapPanel: null,

    init: function() {
        var me = this;

        me.getSummitsStore().on({
            scope: me,
            load : me.onSummitsStoreLoad
        });

        this.control({
            'cf_mappanel': {
                'beforerender': this.onMapPanelBeforeRender
            },
            'summittabpanel': {
                'beforerender': this.onSummitTabPanelBeforeRender
            },
            'summitgrid': {
                'beforeselect': this.onSummitGridBeforeSelect,
                'selectionchange': this.onSummitGridSelectionChange
            }
        }, this);
    },

    onMapPanelBeforeRender: function(mapPanel, options) {
        var me = this;

        var layers = [];

        // OpenLayers object creating
        var wms = new OpenLayers.Layer.WMS(
            "Hydro BNDT",
            "http://ows.geobase.ca/wms/geobase_fr",
            {layers: 'reference:hydro:hydro1m_p,reference:hydro:hydro1m_l'}
        );
        layers.push(wms);

		// OpenLayers object creating
        var wms = new OpenLayers.Layer.WMS(
            "Landsat BNDT",
            "http://ows.geobase.ca/wms/geobase_fr",
            {layers: 'imagery:landsat7'}
        );
        layers.push(wms);
		        // OpenLayers object creating
        var mapserv = new OpenLayers.Layer.WMS(
            "SIGEOM Geologie",
            "http://sigeom.mrn.gouv.qc.ca/SIGEOM_WMS/Request.aspx?",
            {layers: 'Geologie_detaillee_Quebec_50k',transparent:true}, {isBaseLayer: false}
        );
        layers.push(mapserv);
		        // OpenLayers object creating
        var mapserv = new OpenLayers.Layer.WMS(
            "SIGEOM Forage",
            "http://sigeom.mrn.gouv.qc.ca/SIGEOM_WMS/Request.aspx?",
            {layers: 'Forage_diamant',transparent:true}, {isBaseLayer: false}
        );
        layers.push(mapserv);

		// OpenLayers object creating
        var mapserv = new OpenLayers.Layer.WMS(
            "WMS MRB",
            "http://geohack2.mapgears.com/cgi-bin/mapserv?map=/opt/scribeui/application/workspaces/portail/demo/map/demo.map",
            {layers: 'default',transparent:true}, {isBaseLayer: false}
        );
        layers.push(mapserv);

        // create vector layer
        var context = {
            getColor: function(feature) {
                if (feature.attributes.elevation < 2000) {
                    return 'green';
                }
                if (feature.attributes.elevation < 2300) {
                    return 'orange';
                }
                return 'white';
            }
        };
        var template = {
            cursor: "pointer",
            fillOpacity: 0.01,
            fillColor: "${getColor}",
            pointRadius: 5,
            strokeWidth: 1,
            strokeOpacity: 0.01,
            strokeColor: "${getColor}",
            graphicName: "circle"
        };
        var style = new OpenLayers.Style(template, {context: context});
        var vecLayer = new OpenLayers.Layer.Vector("vector", {
            styleMap: new OpenLayers.StyleMap({
                  'default': style,
                  'select': {
            cursor: "pointer",
            fillOpacity: 0.7,
            fillColor: "#12FA29",
            pointRadius: 10,
            strokeWidth: 1,
            strokeOpacity: 1,
            strokeColor: "#12FA29",
            graphicName: "circle"}
            }),
            protocol: new OpenLayers.Protocol.HTTP({
                url: "data/Collar.json",
                format: new OpenLayers.Format.GeoJSON()
            }),
            strategies: [new OpenLayers.Strategy.Fixed()],
			displayInLayerSwitcher:false
        });
        layers.push(vecLayer);

        // manually bind store to layer
        me.getSummitsStore().bind(vecLayer);

        mapPanel.map.addLayers(layers);

        // for dev purpose
        map = mapPanel.map;
        mapPanel = mapPanel;

        // set private properties
        this.mapPanel = mapPanel;
    },

    onLaunch: function() {
        var me = this;

        // for dev purpose
        ctrl = this;
    },

    onSummitsStoreLoad: function(store, records) {
        // do custom stuff on summits load if you want, for example here we
        // zoom to summits extent
        var dataExtent = store.layer.getDataExtent();
        if (dataExtent) {
            store.layer.map.zoomToExtent(dataExtent);
        }
    },

    onSummitTabPanelBeforeRender: function(tabPanel, options) {
        this.summitTabPanel = tabPanel;
    },

    onSummitGridBeforeSelect: function(model, record, index, options) {
        var map = this.mapPanel.map,
            feature = record.raw,
            selectedFeatures = feature.layer.selectedFeatures;

        // feature about to be selected is no in the selected features array,
        // this means it was not selected using the map, but using the grid.
        // If so, recenter the map to feature location.
        if (Ext.Array.indexOf(selectedFeatures, feature) == -1) {
            map.setCenter(feature.geometry.getBounds().getCenterLonLat());
        }
    },

    onSummitGridSelectionChange: function(model, records) {
        if (records[0]) {
            this.summitTabPanel.loadRecord(records[0]);
        }
    }
});
<|endoftext|>


<commit_before>var adt = require('../adt');
var assert = require('assert');

function ok (name, expr) {
  test(name, function () {
    assert.ok(expr);
  });
}

function throws (name, fn, type) {
  test(name, function () {
    assert.throws(fn, type || undefined);
  });
}

suite('Core', function () {
  suite('adt.single()', function () {
    var Foo = adt.single()(adt.__Base__);
    var foo = Foo;
    ok('Instance of adt base class', foo instanceof adt.__Base__);
    ok('`clone` returns the same instance', foo.clone() === foo);
    ok('`equals` matches same instance', foo.equals(Foo));
    ok('`equals` does not match something else', !foo.equals(12));
  });

  var constraint = function (x) {
    if (x === 42) throw new TypeError('42 not allowed');
    return x;
  };

  suite('adt.record(callback)', function () {
    var inner;
    var Foo = adt.record(function (foo, field) {
      inner = foo;
      ok('Class passed to callback', foo === this);
      ok('`field` passed to callback', field === this.field);
      field('a', adt.any);
      field('b');
      field('c', constraint);
      return { d: adt.any };
    })(adt.__Base__);

    var ns = Foo.__names__;
    var cs = Foo.__constraints__;
    ok('Class passed to callback is the same as the one returned', inner === Foo);
    ok('Class has right number of fields', ns.length === 4);
    ok('Class has fields in right order', ns[0] === 'a' && ns[1] === 'b' && ns[2] === 'c' && ns[3] === 'd');
    ok('Class has correct constraints', cs.a === adt.any && cs.b === adt.any && cs.c === constraint && cs.d === adt.any);

    var foo = Foo(1, 2, 3, 4);
    ok('Instance of adt base class', foo instanceof adt.__Base__);
    ok('Instance of constructor', foo instanceof Foo);
    ok('Boilerplate getters', foo.a === 1 && foo.b === 2 && foo.c === 3);
    ok('Index based getters', foo.get(0) === 1 && foo.get(1) === 2 && foo.get(2) === 3);
    ok('Name based getters', foo.get('a') === 1 && foo.get('b') === 2 && foo.get('c') === 3);

    var foo2 = foo.set({ a: 4, b: 5 });
    ok('`set` changes values', foo2.a === 4 && foo2.b === 5 && foo.c === 3);
    ok('`set` returns a copy', foo2 !== foo);
    ok('`equals` matches on an instance with the same values', foo.equals(Foo(1, 2, 3, 4)));
    ok('`equals` does not match on an instance on an instance with different values', !foo.equals(Foo(1, 2, 4, 5)));
    ok('`equals` matches on a deep structure', Foo(foo, 1, 2, 3).equals(Foo(Foo(1, 2, 3, 4), 1, 2, 3)));

    var foo3 = Foo.create({ a: 1, b: 2, c: 3, d: 4 });
    ok('`create` with key value pairs', foo3.a === 1 && foo3.b === 2 && foo3.c === 3);

    var foo4 = Foo(Foo(1, 2, 3, 4), 2, 3, 4);
    var foo5 = foo4.clone();
    ok('`clone` returns a copy', foo4.equals(foo5) && foo4 !== foo5);
    ok('`clone` copies values', foo4.a !== foo5.a);

    var arr = Foo.unapply(foo);
    var obj = Foo.unapplyObj(foo);
    ok('`unapply` returns array representation',
       arr instanceof Array && arr.length === 4 &&
       arr[0] === 1 && arr[1] === 2 && arr[2] === 3 && arr[3] === 4);

    ok('`unapplyObj` returns object representation',
       obj instanceof Object && Object.keys(obj).length === 4 &&
       obj.a === 1 && obj.b === 2 && obj.c === 3 && obj.d === 4);

    var partial = Foo(1, 2);
    ok('Constructor curries', foo.equals(partial(3, 4)));

    test('Constraint throws an error', function () {
      assert.throws(function () {
        new Foo(1, 2, 42, 4);
      }, TypeError);
    });

    Foo.seal();
    ok('`seal` removes `field` and `seal`', !Foo.field && !Foo.seal);
  });

  suite('adt.record(fieldsObj)', function () {
    var Foo = adt.record({
      a: adt.any,
      b: null,
      c: constraint
    })(adt.__Base__);

    var ns = Foo.__names__;
    var cs = Foo.__constraints__;
    ok('Class has right number of fields', ns.length === 3);
    ok('Class has fields in right order', ns[0] === 'a' && ns[1] === 'b' && ns[2] === 'c');
    ok('Class has correct constraints', cs.a === adt.any && cs.b === adt.any && cs.c === constraint);
  });

  suite('adt.record(fieldNames...)', function () {
    var Foo = adt.record('a', 'b', 'c')(adt.__Base__);
    var ns = Foo.__names__;
    var cs = Foo.__constraints__;
    ok('Class has right number of fields', ns.length === 3);
    ok('Class has fields in right order', ns[0] === 'a' && ns[1] === 'b' && ns[2] === 'c');
    ok('Class has correct constraints', cs.a === adt.any && cs.b === adt.any && cs.c === adt.any);
  });

  suite('adt.data(callback)', function () {
    var inner;
    var Foo = adt.data(function (foo, type) {
      inner = foo;
      ok('Class passed to callback', foo === this);
      ok('`type` passed to callback', type === this.type);
      type('A', adt.single());
      type('B');
      type('C', adt.record('a', 'b', 'c'));
      type('D', { a: adt.any, b: null, c: constraint });
      return { E: adt.single() };
    });

    var a = Foo.A, b = Foo.B, c = Foo.C(1, 2, 3), d = Foo.D(4, 5, 6), e = Foo.E;
    ok('Class passed to callback is the same as the one returned', inner === Foo);
    ok('Instance of base class',
       a instanceof adt.__Base__ &&
       b instanceof adt.__Base__ &&
       c instanceof adt.__Base__ &&
       d instanceof adt.__Base__ &&
       e instanceof adt.__Base__);
    ok('Instance of type constructor',
       a instanceof Foo &&
       b instanceof Foo &&
       c instanceof Foo &&
       d instanceof Foo &&
       e instanceof Foo);

    ok('Boilerplate type checking',
       a.isA && !a.isB && !a.isC && !a.isD && !a.isE &&
       !b.isA && b.isB && !b.isC && !b.isD && !b.isE &&
       !c.isA && !c.isB && c.isC && !c.isD && !c.isE &&
       !d.isA && !d.isB && !d.isC && d.isD && !d.isE &&
       !e.isA && !e.isB && !e.isC && !e.isD && e.isE);

    ok('Correct `className`',
       Foo.A.constructor.className === 'A' && Foo.B.constructor.className === 'B' &&
       Foo.C.className === 'C' && Foo.D.className === 'D' &&
       Foo.E.constructor.className === 'E');

    var ns = Foo.D.__names__;
    var cs = Foo.D.__constraints__;
    ok('Class has right number of fields', ns.length === 3);
    ok('Class has fields in right order', ns[0] === 'a' && ns[1] === 'b' && ns[2] === 'c');
    ok('Class has correct constraints', cs.a === adt.any && cs.b === adt.any && cs.c === constraint);

    Foo.seal();
    ok('`seal` removes `type` and `seal`', !Foo.type && !Foo.seal);
    ok('`seal` calls `seal` on types', !Foo.A.seal && !Foo.A.field);
  });

  suite('adt.data(typesObj)', function () {
    var Foo = adt.data({
      A: adt.single(),
      B: adt.single()
    });

    var a = Foo.A, b = Foo.B;
    ok('Instance of base class', a instanceof adt.__Base__ && b instanceof adt.__Base__);
    ok('Instance of type constructor', a instanceof Foo && b instanceof Foo);
    ok('Correct `className`', Foo.A.constructor.className === 'A' && Foo.B.constructor.className === 'B');
  });

  suite('adt.enumeration(typeNames...)', function () {
    var Foo = adt.enumeration('A', 'B');
    var a = Foo.A, b = Foo.B;

    ok('`lt`', a.lt(b));
    ok('not `lt`', !b.lt(a));
    ok('`lte`', a.lte(a) && a.lte(b));
    ok('not `lte`', !b.lte(a));
    ok('`gt`', b.gt(a));
    ok('not `gt`', !a.gt(b));
    ok('`gte`', b.gte(a) && a.gte(a));
    ok('not `gte`', !a.gte(b));
    ok('`eq`', a.eq(a));
    ok('`neq`', a.neq(b));
  });

  suite('adt.newtype', function () {
    var Foo = adt.newtype('Foo', { a: adt.any, b: adt.any });
    var foo = Foo(1, 2);
    ok('Typecheck', foo.isFoo);
    ok('Fields', foo.a === 1 && foo.b === 2);
  });

  suite('adt.only', function () {
    ok('Number', adt.only(Number)(42));
    ok('String', adt.only(String)('foo'));
    ok('Literal', adt.only('foo')('foo'));
    throws('Number throws', function () { adt.only(Number)(null) }, TypeError);
    throws('String throws', function () { adt.only(String)(null) }, TypeError);
    throws('Literal throws', function () { adt.only('foo')('bar') }, TypeError);

    var mult = adt.only(String, Number, null);
    ok('Multiple types', mult('foo') && mult(42) && mult(null) === null);
    throws('Multiple throws', function () { mult(true); }, TypeError);
  });
});
<commit_msg>Added test case for using  on enums.<commit_after>var adt = require('../adt');
var assert = require('assert');

function ok (name, expr) {
  test(name, function () {
    assert.ok(expr);
  });
}

function throws (name, fn, type) {
  test(name, function () {
    assert.throws(fn, type || undefined);
  });
}

suite('Core', function () {
  suite('adt.single()', function () {
    var Foo = adt.single()(adt.__Base__);
    var foo = Foo;
    ok('Instance of adt base class', foo instanceof adt.__Base__);
    ok('`clone` returns the same instance', foo.clone() === foo);
    ok('`equals` matches same instance', foo.equals(Foo));
    ok('`equals` does not match something else', !foo.equals(12));
  });

  var constraint = function (x) {
    if (x === 42) throw new TypeError('42 not allowed');
    return x;
  };

  suite('adt.record(callback)', function () {
    var inner;
    var Foo = adt.record(function (foo, field) {
      inner = foo;
      ok('Class passed to callback', foo === this);
      ok('`field` passed to callback', field === this.field);
      field('a', adt.any);
      field('b');
      field('c', constraint);
      return { d: adt.any };
    })(adt.__Base__);

    var ns = Foo.__names__;
    var cs = Foo.__constraints__;
    ok('Class passed to callback is the same as the one returned', inner === Foo);
    ok('Class has right number of fields', ns.length === 4);
    ok('Class has fields in right order', ns[0] === 'a' && ns[1] === 'b' && ns[2] === 'c' && ns[3] === 'd');
    ok('Class has correct constraints', cs.a === adt.any && cs.b === adt.any && cs.c === constraint && cs.d === adt.any);

    var foo = Foo(1, 2, 3, 4);
    ok('Instance of adt base class', foo instanceof adt.__Base__);
    ok('Instance of constructor', foo instanceof Foo);
    ok('Boilerplate getters', foo.a === 1 && foo.b === 2 && foo.c === 3);
    ok('Index based getters', foo.get(0) === 1 && foo.get(1) === 2 && foo.get(2) === 3);
    ok('Name based getters', foo.get('a') === 1 && foo.get('b') === 2 && foo.get('c') === 3);

    var foo2 = foo.set({ a: 4, b: 5 });
    ok('`set` changes values', foo2.a === 4 && foo2.b === 5 && foo.c === 3);
    ok('`set` returns a copy', foo2 !== foo);
    ok('`equals` matches on an instance with the same values', foo.equals(Foo(1, 2, 3, 4)));
    ok('`equals` does not match on an instance on an instance with different values', !foo.equals(Foo(1, 2, 4, 5)));
    ok('`equals` matches on a deep structure', Foo(foo, 1, 2, 3).equals(Foo(Foo(1, 2, 3, 4), 1, 2, 3)));

    var foo3 = Foo.create({ a: 1, b: 2, c: 3, d: 4 });
    ok('`create` with key value pairs', foo3.a === 1 && foo3.b === 2 && foo3.c === 3);

    var foo4 = Foo(Foo(1, 2, 3, 4), 2, 3, 4);
    var foo5 = foo4.clone();
    ok('`clone` returns a copy', foo4.equals(foo5) && foo4 !== foo5);
    ok('`clone` copies values', foo4.a !== foo5.a);

    var arr = Foo.unapply(foo);
    var obj = Foo.unapplyObj(foo);
    ok('`unapply` returns array representation',
       arr instanceof Array && arr.length === 4 &&
       arr[0] === 1 && arr[1] === 2 && arr[2] === 3 && arr[3] === 4);

    ok('`unapplyObj` returns object representation',
       obj instanceof Object && Object.keys(obj).length === 4 &&
       obj.a === 1 && obj.b === 2 && obj.c === 3 && obj.d === 4);

    var partial = Foo(1, 2);
    ok('Constructor curries', foo.equals(partial(3, 4)));

    test('Constraint throws an error', function () {
      assert.throws(function () {
        new Foo(1, 2, 42, 4);
      }, TypeError);
    });

    Foo.seal();
    ok('`seal` removes `field` and `seal`', !Foo.field && !Foo.seal);
  });

  suite('adt.record(fieldsObj)', function () {
    var Foo = adt.record({
      a: adt.any,
      b: null,
      c: constraint
    })(adt.__Base__);

    var ns = Foo.__names__;
    var cs = Foo.__constraints__;
    ok('Class has right number of fields', ns.length === 3);
    ok('Class has fields in right order', ns[0] === 'a' && ns[1] === 'b' && ns[2] === 'c');
    ok('Class has correct constraints', cs.a === adt.any && cs.b === adt.any && cs.c === constraint);
  });

  suite('adt.record(fieldNames...)', function () {
    var Foo = adt.record('a', 'b', 'c')(adt.__Base__);
    var ns = Foo.__names__;
    var cs = Foo.__constraints__;
    ok('Class has right number of fields', ns.length === 3);
    ok('Class has fields in right order', ns[0] === 'a' && ns[1] === 'b' && ns[2] === 'c');
    ok('Class has correct constraints', cs.a === adt.any && cs.b === adt.any && cs.c === adt.any);
  });

  suite('adt.data(callback)', function () {
    var inner;
    var Foo = adt.data(function (foo, type) {
      inner = foo;
      ok('Class passed to callback', foo === this);
      ok('`type` passed to callback', type === this.type);
      type('A', adt.single());
      type('B');
      type('C', adt.record('a', 'b', 'c'));
      type('D', { a: adt.any, b: null, c: constraint });
      return { E: adt.single() };
    });

    var a = Foo.A, b = Foo.B, c = Foo.C(1, 2, 3), d = Foo.D(4, 5, 6), e = Foo.E;
    ok('Class passed to callback is the same as the one returned', inner === Foo);
    ok('Instance of base class',
       a instanceof adt.__Base__ &&
       b instanceof adt.__Base__ &&
       c instanceof adt.__Base__ &&
       d instanceof adt.__Base__ &&
       e instanceof adt.__Base__);
    ok('Instance of type constructor',
       a instanceof Foo &&
       b instanceof Foo &&
       c instanceof Foo &&
       d instanceof Foo &&
       e instanceof Foo);

    ok('Boilerplate type checking',
       a.isA && !a.isB && !a.isC && !a.isD && !a.isE &&
       !b.isA && b.isB && !b.isC && !b.isD && !b.isE &&
       !c.isA && !c.isB && c.isC && !c.isD && !c.isE &&
       !d.isA && !d.isB && !d.isC && d.isD && !d.isE &&
       !e.isA && !e.isB && !e.isC && !e.isD && e.isE);

    ok('Correct `className`',
       Foo.A.constructor.className === 'A' && Foo.B.constructor.className === 'B' &&
       Foo.C.className === 'C' && Foo.D.className === 'D' &&
       Foo.E.constructor.className === 'E');

    var ns = Foo.D.__names__;
    var cs = Foo.D.__constraints__;
    ok('Class has right number of fields', ns.length === 3);
    ok('Class has fields in right order', ns[0] === 'a' && ns[1] === 'b' && ns[2] === 'c');
    ok('Class has correct constraints', cs.a === adt.any && cs.b === adt.any && cs.c === constraint);

    Foo.seal();
    ok('`seal` removes `type` and `seal`', !Foo.type && !Foo.seal);
    ok('`seal` calls `seal` on types', !Foo.A.seal && !Foo.A.field);
  });

  suite('adt.data(typesObj)', function () {
    var Foo = adt.data({
      A: adt.single(),
      B: adt.single()
    });

    var a = Foo.A, b = Foo.B;
    ok('Instance of base class', a instanceof adt.__Base__ && b instanceof adt.__Base__);
    ok('Instance of type constructor', a instanceof Foo && b instanceof Foo);
    ok('Correct `className`', Foo.A.constructor.className === 'A' && Foo.B.constructor.className === 'B');
  });

  suite('adt.enumeration(typeNames...)', function () {
    var Foo = adt.enumeration('A', 'B');
    var a = Foo.A, b = Foo.B;

    ok('`lt`', a.lt(b));
    ok('not `lt`', !b.lt(a));
    ok('`lte`', a.lte(a) && a.lte(b));
    ok('not `lte`', !b.lte(a));
    ok('`gt`', b.gt(a));
    ok('not `gt`', !a.gt(b));
    ok('`gte`', b.gte(a) && a.gte(a));
    ok('not `gte`', !a.gte(b));
    ok('`eq`', a.eq(a));
    ok('`neq`', a.neq(b));
  });

  suite('adt.enumeration.type()', function () {
    var Foo = adt.enumeration('A');
    Foo.type('B');
    var a = Foo.A, b = Foo.B;

    ok('B is greater than A', b.gt(a));
  });

  suite('adt.newtype', function () {
    var Foo = adt.newtype('Foo', { a: adt.any, b: adt.any });
    var foo = Foo(1, 2);
    ok('Typecheck', foo.isFoo);
    ok('Fields', foo.a === 1 && foo.b === 2);
  });

  suite('adt.only', function () {
    ok('Number', adt.only(Number)(42));
    ok('String', adt.only(String)('foo'));
    ok('Literal', adt.only('foo')('foo'));
    throws('Number throws', function () { adt.only(Number)(null) }, TypeError);
    throws('String throws', function () { adt.only(String)(null) }, TypeError);
    throws('Literal throws', function () { adt.only('foo')('bar') }, TypeError);

    var mult = adt.only(String, Number, null);
    ok('Multiple types', mult('foo') && mult(42) && mult(null) === null);
    throws('Multiple throws', function () { mult(true); }, TypeError);
  });
});
<|endoftext|>


<commit_before>var assert = require('assert'),
    Blast;

describe('Deck', function() {

	before(function() {
		Blast  = require('../index.js')();
	});

	describe('new Deck()', function() {
		it('should return an empty deck', function() {
			var d = new Deck();

			assert.equal(0, d.insertCount);
		});
	});

	describe('#set(key, value, weight)', function() {
		it('should register the value with the given key and optional weight', function() {

			var d = new Deck();
			d.set('mykey', 47);

			assert.equal(47, d.get('mykey'));
		});
	});

	describe('#get(key)', function() {
		it('should return the value of the wanted key', function() {

			var d = new Deck();
			d.set('mykey', 47);

			assert.equal(47, d.get('mykey'));
		});
	});

	describe('#getById(key)', function() {
		it('should return the value of the wanted id', function() {

			var d = new Deck();
			d.set('mykey', 47);

			assert.equal(47, d.getById(0));
		});
	});

	describe('#getSorted()', function() {
		it('should return the sorted values by insert order when added without weight', function() {

			var d = new Deck();

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			assert.equal('a,b,c,d', d.getSorted().join(','));
		});

		it('should return the sorted values by weight', function() {

			var d = new Deck();

			d.push('c', 200);
			d.set('b', 'b', 300);
			d.push('d', 100);
			d.set('a', 'a', 400);

			assert.equal('a,b,c,d', d.getSorted().join(','));
		});
	});

	describe('#forEach(fnc)', function() {
		it('iterate over the sorted items with a function callback', function() {

			var d = new Deck(),
			    result = '';

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			d.forEach(function(val) {
				result += val;
			});

			assert.equal('abcd', result);
		});
	});

	describe('#some(fnc)', function() {
		it('iterate over the sorted items with a function callback, break on returned true', function() {

			var d = new Deck(),
			    result = '';

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			d.some(function(val) {
				result += val;

				if (result.length == 2) return true;
			});

			assert.equal('ab', result);
		});
	});

	describe('#every(fnc)', function() {
		it('iterate over the sorted items with a function callback, break on returned false', function() {

			var d = new Deck(),
			    result = '';

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			d.every(function(val) {
				result += val;

				if (result.length == 2) return false;
			});

			assert.equal('ab', result);
		});
	});

	describe('#createIterator()', function() {
		it('return an iterator', function() {

			var d = new Deck(),
			    iter;

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			iter = d.createIterator();

			assert.equal('Iterator', iter.constructor.name);
		});
	});

	describe('#createIteratorItems()', function() {
		it('return an iterator', function() {

			var d = new Deck(),
			    iter;

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			iter = d.createIteratorItems();

			assert.equal('Iterator', iter.constructor.name);
		});
	});

});<commit_msg>test: add Deck#clone() unit tests<commit_after>var assert = require('assert'),
    Blast;

describe('Deck', function() {

	before(function() {
		Blast  = require('../index.js')();
	});

	describe('new Deck()', function() {
		it('should return an empty deck', function() {
			var d = new Deck();

			assert.equal(0, d.insertCount);
		});
	});

	describe('#set(key, value, weight)', function() {
		it('should register the value with the given key and optional weight', function() {

			var d = new Deck();
			d.set('mykey', 47);

			assert.equal(47, d.get('mykey'));
		});
	});

	describe('#get(key)', function() {
		it('should return the value of the wanted key', function() {

			var d = new Deck();
			d.set('mykey', 47);

			assert.equal(47, d.get('mykey'));
		});
	});

	describe('#getById(key)', function() {
		it('should return the value of the wanted id', function() {

			var d = new Deck();
			d.set('mykey', 47);

			assert.equal(47, d.getById(0));
		});
	});

	describe('#getSorted()', function() {
		it('should return the sorted values by insert order when added without weight', function() {

			var d = new Deck();

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			assert.equal('a,b,c,d', d.getSorted().join(','));
		});

		it('should return the sorted values by weight', function() {

			var d = new Deck();

			d.push('c', 200);
			d.set('b', 'b', 300);
			d.push('d', 100);
			d.set('a', 'a', 400);

			assert.equal('a,b,c,d', d.getSorted().join(','));
		});
	});

	describe('#forEach(fnc)', function() {
		it('iterate over the sorted items with a function callback', function() {

			var d = new Deck(),
			    result = '';

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			d.forEach(function(val) {
				result += val;
			});

			assert.equal('abcd', result);
		});
	});

	describe('#some(fnc)', function() {
		it('iterate over the sorted items with a function callback, break on returned true', function() {

			var d = new Deck(),
			    result = '';

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			d.some(function(val) {
				result += val;

				if (result.length == 2) return true;
			});

			assert.equal('ab', result);
		});
	});

	describe('#every(fnc)', function() {
		it('iterate over the sorted items with a function callback, break on returned false', function() {

			var d = new Deck(),
			    result = '';

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			d.every(function(val) {
				result += val;

				if (result.length == 2) return false;
			});

			assert.equal('ab', result);
		});
	});

	describe('#createIterator()', function() {
		it('return an iterator', function() {

			var d = new Deck(),
			    iter;

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			iter = d.createIterator();

			assert.equal('Iterator', iter.constructor.name);
		});
	});

	describe('#createIteratorItems()', function() {
		it('return an iterator', function() {

			var d = new Deck(),
			    iter;

			d.set('a', 'a');
			d.set('b', 'b');
			d.push('c');
			d.push('d');

			iter = d.createIteratorItems();

			assert.equal('Iterator', iter.constructor.name);
		});
	});

	describe('#clone()', function() {

		it('should clone the deck (but not the values)', function() {

			var temp,
			    clone,
			    ori = new Deck(),
			    i;

			ori.push('a', 10);
			ori.push('b', 9);
			ori.push('0', 900);

			temp = ori.getSorted();

			// Test sorting before (heavier values come first)
			assert.equal('0,a,b', temp.join(','));

			// Clone the original deck
			clone = ori.clone();

			// Fuck with the original object's weight
			for (i = 0; i < ori.array.length; i++) {
				ori.array[i].weight = 10;
			}

			ori.sorted = ori.sortedItems = false;

			temp = ori.getSorted();

			// Everything has the same weight, so order should be insertion order
			assert.equal('a,b,0', temp.join(','));

			// Now get the sorted items of the clone
			temp = clone.getSorted();

			// Those should still be the original order
			assert.equal('0,a,b', temp.join(','));
		});
	});
});<|endoftext|>