<commit_before>import Controller from '@ember/controller';
import config from 'ghost-admin/config/environment';
import fetch from 'fetch';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import moment from 'moment';
import {A} from '@ember/array';
import {action} from '@ember/object';
import {ghPluralize} from 'ghost-admin/helpers/gh-pluralize';
import {resetQueryParams} from 'ghost-admin/helpers/reset-query-params';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

const PAID_PARAMS = [{
    name: 'All members',
    value: null
}, {
    name: 'Free members',
    value: 'false'
}, {
    name: 'Paid members',
    value: 'true'
}];

export default class MembersController extends Controller {
    @service ajax;
    @service config;
    @service ellaSparse;
    @service feature;
    @service ghostPaths;
    @service membersStats;
    @service router;
    @service store;

    queryParams = [
        'label',
        {paidParam: 'paid'},
        {searchParam: 'search'},
        {orderParam: 'order'},
        {filterParam: 'filter'}
    ];

    @tracked members = A([]);
    @tracked searchText = '';
    @tracked searchParam = '';
    @tracked filterParam = null;
    @tracked paidParam = null;
    @tracked label = null;
    @tracked orderParam = null;
    @tracked modalLabel = null;
    @tracked showLabelModal = false;
    @tracked showDeleteMembersModal = false;

    @tracked _availableLabels = A([]);

    paidParams = PAID_PARAMS;

    constructor() {
        super(...arguments);
        this._availableLabels = this.store.peekAll('label');

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

    // Computed properties -----------------------------------------------------

    get listHeader() {
        let {searchText, selectedLabel, members} = this;

        if (members.loading) {
            return 'Loading...';
        }

        if (searchText) {
            return 'Search result';
        }

        let count = ghPluralize(members.length, 'member');

        if (selectedLabel && selectedLabel.slug) {
            if (members.length > 1) {
                return `${count} match current filter`;
            } else {
                return `${count} matches current filter`;
            }
        }

        return count;
    }

    get showingAll() {
        return !this.searchParam && !this.paidParam && !this.label && !this.filterParam;
    }

    get availableOrders() {
        // don't return anything if email analytics is disabled because
        // we don't want to show an order dropdown with only a single option

        if (this.feature.get('emailAnalytics')) {
            return [{
                name: 'Newest',
                value: null
            }, {
                name: 'Open rate',
                value: 'email_open_rate'
            }];
        }

        return [];
    }

    get selectedOrder() {
        return this.availableOrders.find(order => order.value === this.orderParam);
    }

    get availableLabels() {
        let labels = this._availableLabels
            .filter(label => !label.isNew)
            .filter(label => label.id !== null)
            .sort((labelA, labelB) => labelA.name.localeCompare(labelB.name, undefined, {ignorePunctuation: true}));
        let options = labels.toArray();

        options.unshiftObject({name: 'All labels', slug: null});

        return options;
    }

    get selectedLabel() {
        let {label, availableLabels} = this;
        return availableLabels.findBy('slug', label);
    }

    get labelModalData() {
        let label = this.modalLabel;
        let labels = this.availableLabels;

        return {
            label,
            labels
        };
    }

    get selectedPaidParam() {
        return this.paidParams.findBy('value', this.paidParam) || {value: '!unknown'};
    }

    get isFiltered() {
        return !!(this.label || this.paidParam || this.searchParam || this.filterParam);
    }

    getApiQueryObject({params, extraFilters = []} = {}) {
        let {label, paidParam, searchParam, filterParam} = params ? params : this;

        let filters = [];

        filters = filters.concat(extraFilters);

        if (label) {
            filters.push(`label:'${label}'`);
        }

        if (paidParam !== null) {
            if (paidParam === 'true') {
                filters.push('status:-free');
            } else {
                filters.push('status:free');
            }
        }

        if (filterParam && this.feature.get('membersFiltering')) {
            filters.push(filterParam);
        }

        let searchQuery = searchParam ? {search: searchParam} : {};

        return Object.assign({}, {filter: filters.join('+')}, searchQuery);
    }

    // Actions -----------------------------------------------------------------

    @action
    refreshData() {
        this.fetchMembersTask.perform();
        this.fetchLabelsTask.perform();
        this.membersStats.invalidate();
        this.membersStats.fetchCounts();
    }

    @action
    changeOrder(order) {
        this.orderParam = order.value;
    }

    @action
    search(e) {
        this.searchTask.perform(e.target.value);
    }

    @action
    exportData() {
        let exportUrl = ghostPaths().url.api('members/upload');
        let downloadParams = new URLSearchParams(this.getApiQueryObject());
        downloadParams.set('limit', 'all');

        let iframe = document.getElementById('iframeDownload');

        if (!iframe) {
            iframe = document.createElement('iframe');
            iframe.id = 'iframeDownload';
            iframe.style.display = 'none';
            document.body.append(iframe);
        }
        iframe.setAttribute('src', `${exportUrl}?${downloadParams.toString()}`);
    }

    @action
    changeLabel(label, e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        this.label = label.slug;
    }

    @action
    addLabel(e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        const newLabel = this.store.createRecord('label');
        this.modalLabel = newLabel;
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    editLabel(label, e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        let modalLabel = this.availableLabels.findBy('slug', label);
        this.modalLabel = modalLabel;
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    toggleLabelModal() {
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    changePaidParam(paid) {
        this.paidParam = paid.value;
    }

    @action
    toggleDeleteMembersModal() {
        this.showDeleteMembersModal = !this.showDeleteMembersModal;
    }

    @action
    deleteMembers() {
        return this.deleteMembersTask.perform();
    }

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

    @task({restartable: true})
    *searchTask(query) {
        yield timeout(250); // debounce
        this.searchParam = query;
    }

    @task
    *fetchLabelsTask() {
        yield this.store.query('label', {limit: 'all'});
    }

    @task({restartable: true})
    *fetchMembersTask(params) {
        // params is undefined when called as a "refresh" of the model
        let {label, paidParam, searchParam, orderParam, filterParam} = typeof params === 'undefined' ? this : params;

        if (!searchParam) {
            this.resetSearch();
        }

        // use a fixed created_at date so that subsequent pages have a consistent index
        let startDate = new Date();

        // bypass the stale data shortcut if params change
        let forceReload = !params
            || label !== this._lastLabel
            || paidParam !== this._lastPaidParam
            || searchParam !== this._lastSearchParam
            || orderParam !== this._orderParam
            || filterParam !== this._lastFilterParam;
        this._lastLabel = label;
        this._lastPaidParam = paidParam;
        this._lastSearchParam = searchParam;
        this._lastOrderParam = orderParam;
        this._lastFilterParam = filterParam;

        // unless we have a forced reload, do not re-fetch the members list unless it's more than a minute old
        // keeps navigation between list->details->list snappy
        if (!forceReload && this._startDate && !(this._startDate - startDate > 1 * 60 * 1000)) {
            return this.members;
        }

        this._startDate = startDate;

        this.members = yield this.ellaSparse.array((range = {}, query = {}) => {
            const searchQuery = this.getApiQueryObject({
                params,
                extraFilters: [`created_at:<='${moment.utc(this._startDate).format('YYYY-MM-DD HH:mm:ss')}'`]
            });
            const order = orderParam ? `${orderParam} desc` : `created_at desc`;

            query = Object.assign({
                order,
                limit: range.length,
                page: range.page
            }, searchQuery, query);

            return this.store.query('member', query).then((result) => {
                return {
                    data: result,
                    total: result.meta.pagination.total
                };
            });
        }, {
            limit: 50
        });
    }

    @task({drop: true})
    *deleteMembersTask() {
        const query = new URLSearchParams(this.getApiQueryObject());

        // Trigger download before deleting. Uses the CSV export endpoint but
        // needs to fetch the file and trigger a download directly rather than
        // via an iframe. The iframe approach can't tell us when a download has
        // started/finished meaning we could end up deleting the data before exporting it
        const exportUrl = ghostPaths().url.api('members/upload');
        const exportParams = new URLSearchParams(this.getApiQueryObject());
        exportParams.set('limit', 'all');

        yield fetch(exportUrl, {method: 'GET'})
            .then(res => res.blob())
            .then((blob) => {
                const blobUrl = window.URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = blobUrl;
                a.download = `members.${moment().format('YYYY-MM-DD')}.csv`;
                document.body.appendChild(a);
                if (!this.isTesting) {
                    a.click();
                }
                a.remove();
                URL.revokeObjectURL(blobUrl);
            });

        // backup downloaded, continue with deletion

        const deleteUrl = `${this.ghostPaths.url.api('members')}?${query}`;

        // response contains details of which members failed to be deleted
        const response = yield this.ajax.del(deleteUrl);

        // reset and reload
        this.store.unloadAll('member');
        this.router.transitionTo('members.index', {queryParams: Object.assign(resetQueryParams('members.index'))});
        this.membersStats.invalidate();
        this.membersStats.fetchCounts();

        return response.meta;
    }

    // Internal ----------------------------------------------------------------

    resetSearch() {
        this.searchText = '';
    }

    reload(params) {
        this.membersStats.invalidate();
        this.membersStats.fetchCounts();
        this.fetchMembersTask.perform(params);
    }
}
<commit_msg>Fixed incorrect order param comparison in force reload<commit_after>import Controller from '@ember/controller';
import config from 'ghost-admin/config/environment';
import fetch from 'fetch';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import moment from 'moment';
import {A} from '@ember/array';
import {action} from '@ember/object';
import {ghPluralize} from 'ghost-admin/helpers/gh-pluralize';
import {resetQueryParams} from 'ghost-admin/helpers/reset-query-params';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

const PAID_PARAMS = [{
    name: 'All members',
    value: null
}, {
    name: 'Free members',
    value: 'false'
}, {
    name: 'Paid members',
    value: 'true'
}];

export default class MembersController extends Controller {
    @service ajax;
    @service config;
    @service ellaSparse;
    @service feature;
    @service ghostPaths;
    @service membersStats;
    @service router;
    @service store;

    queryParams = [
        'label',
        {paidParam: 'paid'},
        {searchParam: 'search'},
        {orderParam: 'order'},
        {filterParam: 'filter'}
    ];

    @tracked members = A([]);
    @tracked searchText = '';
    @tracked searchParam = '';
    @tracked filterParam = null;
    @tracked paidParam = null;
    @tracked label = null;
    @tracked orderParam = null;
    @tracked modalLabel = null;
    @tracked showLabelModal = false;
    @tracked showDeleteMembersModal = false;

    @tracked _availableLabels = A([]);

    paidParams = PAID_PARAMS;

    constructor() {
        super(...arguments);
        this._availableLabels = this.store.peekAll('label');

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

    // Computed properties -----------------------------------------------------

    get listHeader() {
        let {searchText, selectedLabel, members} = this;

        if (members.loading) {
            return 'Loading...';
        }

        if (searchText) {
            return 'Search result';
        }

        let count = ghPluralize(members.length, 'member');

        if (selectedLabel && selectedLabel.slug) {
            if (members.length > 1) {
                return `${count} match current filter`;
            } else {
                return `${count} matches current filter`;
            }
        }

        return count;
    }

    get showingAll() {
        return !this.searchParam && !this.paidParam && !this.label && !this.filterParam;
    }

    get availableOrders() {
        // don't return anything if email analytics is disabled because
        // we don't want to show an order dropdown with only a single option

        if (this.feature.get('emailAnalytics')) {
            return [{
                name: 'Newest',
                value: null
            }, {
                name: 'Open rate',
                value: 'email_open_rate'
            }];
        }

        return [];
    }

    get selectedOrder() {
        return this.availableOrders.find(order => order.value === this.orderParam);
    }

    get availableLabels() {
        let labels = this._availableLabels
            .filter(label => !label.isNew)
            .filter(label => label.id !== null)
            .sort((labelA, labelB) => labelA.name.localeCompare(labelB.name, undefined, {ignorePunctuation: true}));
        let options = labels.toArray();

        options.unshiftObject({name: 'All labels', slug: null});

        return options;
    }

    get selectedLabel() {
        let {label, availableLabels} = this;
        return availableLabels.findBy('slug', label);
    }

    get labelModalData() {
        let label = this.modalLabel;
        let labels = this.availableLabels;

        return {
            label,
            labels
        };
    }

    get selectedPaidParam() {
        return this.paidParams.findBy('value', this.paidParam) || {value: '!unknown'};
    }

    get isFiltered() {
        return !!(this.label || this.paidParam || this.searchParam || this.filterParam);
    }

    getApiQueryObject({params, extraFilters = []} = {}) {
        let {label, paidParam, searchParam, filterParam} = params ? params : this;

        let filters = [];

        filters = filters.concat(extraFilters);

        if (label) {
            filters.push(`label:'${label}'`);
        }

        if (paidParam !== null) {
            if (paidParam === 'true') {
                filters.push('status:-free');
            } else {
                filters.push('status:free');
            }
        }

        if (filterParam && this.feature.get('membersFiltering')) {
            filters.push(filterParam);
        }

        let searchQuery = searchParam ? {search: searchParam} : {};

        return Object.assign({}, {filter: filters.join('+')}, searchQuery);
    }

    // Actions -----------------------------------------------------------------

    @action
    refreshData() {
        this.fetchMembersTask.perform();
        this.fetchLabelsTask.perform();
        this.membersStats.invalidate();
        this.membersStats.fetchCounts();
    }

    @action
    changeOrder(order) {
        this.orderParam = order.value;
    }

    @action
    search(e) {
        this.searchTask.perform(e.target.value);
    }

    @action
    exportData() {
        let exportUrl = ghostPaths().url.api('members/upload');
        let downloadParams = new URLSearchParams(this.getApiQueryObject());
        downloadParams.set('limit', 'all');

        let iframe = document.getElementById('iframeDownload');

        if (!iframe) {
            iframe = document.createElement('iframe');
            iframe.id = 'iframeDownload';
            iframe.style.display = 'none';
            document.body.append(iframe);
        }
        iframe.setAttribute('src', `${exportUrl}?${downloadParams.toString()}`);
    }

    @action
    changeLabel(label, e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        this.label = label.slug;
    }

    @action
    addLabel(e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        const newLabel = this.store.createRecord('label');
        this.modalLabel = newLabel;
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    editLabel(label, e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        let modalLabel = this.availableLabels.findBy('slug', label);
        this.modalLabel = modalLabel;
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    toggleLabelModal() {
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    changePaidParam(paid) {
        this.paidParam = paid.value;
    }

    @action
    toggleDeleteMembersModal() {
        this.showDeleteMembersModal = !this.showDeleteMembersModal;
    }

    @action
    deleteMembers() {
        return this.deleteMembersTask.perform();
    }

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

    @task({restartable: true})
    *searchTask(query) {
        yield timeout(250); // debounce
        this.searchParam = query;
    }

    @task
    *fetchLabelsTask() {
        yield this.store.query('label', {limit: 'all'});
    }

    @task({restartable: true})
    *fetchMembersTask(params) {
        // params is undefined when called as a "refresh" of the model
        let {label, paidParam, searchParam, orderParam, filterParam} = typeof params === 'undefined' ? this : params;

        if (!searchParam) {
            this.resetSearch();
        }

        // use a fixed created_at date so that subsequent pages have a consistent index
        let startDate = new Date();

        // bypass the stale data shortcut if params change
        let forceReload = !params
            || label !== this._lastLabel
            || paidParam !== this._lastPaidParam
            || searchParam !== this._lastSearchParam
            || orderParam !== this._lastOrderParam
            || filterParam !== this._lastFilterParam;
        this._lastLabel = label;
        this._lastPaidParam = paidParam;
        this._lastSearchParam = searchParam;
        this._lastOrderParam = orderParam;
        this._lastFilterParam = filterParam;

        // unless we have a forced reload, do not re-fetch the members list unless it's more than a minute old
        // keeps navigation between list->details->list snappy
        if (!forceReload && this._startDate && !(this._startDate - startDate > 1 * 60 * 1000)) {
            return this.members;
        }

        this._startDate = startDate;

        this.members = yield this.ellaSparse.array((range = {}, query = {}) => {
            const searchQuery = this.getApiQueryObject({
                params,
                extraFilters: [`created_at:<='${moment.utc(this._startDate).format('YYYY-MM-DD HH:mm:ss')}'`]
            });
            const order = orderParam ? `${orderParam} desc` : `created_at desc`;

            query = Object.assign({
                order,
                limit: range.length,
                page: range.page
            }, searchQuery, query);

            return this.store.query('member', query).then((result) => {
                return {
                    data: result,
                    total: result.meta.pagination.total
                };
            });
        }, {
            limit: 50
        });
    }

    @task({drop: true})
    *deleteMembersTask() {
        const query = new URLSearchParams(this.getApiQueryObject());

        // Trigger download before deleting. Uses the CSV export endpoint but
        // needs to fetch the file and trigger a download directly rather than
        // via an iframe. The iframe approach can't tell us when a download has
        // started/finished meaning we could end up deleting the data before exporting it
        const exportUrl = ghostPaths().url.api('members/upload');
        const exportParams = new URLSearchParams(this.getApiQueryObject());
        exportParams.set('limit', 'all');

        yield fetch(exportUrl, {method: 'GET'})
            .then(res => res.blob())
            .then((blob) => {
                const blobUrl = window.URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = blobUrl;
                a.download = `members.${moment().format('YYYY-MM-DD')}.csv`;
                document.body.appendChild(a);
                if (!this.isTesting) {
                    a.click();
                }
                a.remove();
                URL.revokeObjectURL(blobUrl);
            });

        // backup downloaded, continue with deletion

        const deleteUrl = `${this.ghostPaths.url.api('members')}?${query}`;

        // response contains details of which members failed to be deleted
        const response = yield this.ajax.del(deleteUrl);

        // reset and reload
        this.store.unloadAll('member');
        this.router.transitionTo('members.index', {queryParams: Object.assign(resetQueryParams('members.index'))});
        this.membersStats.invalidate();
        this.membersStats.fetchCounts();

        return response.meta;
    }

    // Internal ----------------------------------------------------------------

    resetSearch() {
        this.searchText = '';
    }

    reload(params) {
        this.membersStats.invalidate();
        this.membersStats.fetchCounts();
        this.fetchMembersTask.perform(params);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015. Author: Jeffrey Hing. All Rights Reserved.
 *
 * widthWatch directive.
 *
 * @example
 *
 *

 *
...

 *
...

 *

 *
 * @example
 *
 *

 *
 * @example
 *
 *

 *
...

 *
...

 *

 *
 */
'use strict';

//-------------------------------------
// Dependencies and variables
//-------------------------------------

var widthQueryClass = require('./widthQueryClass/directive');

// Private model name.
var MODEL = '_widthQueryDirective';

//-------------------------------------
// Exports
//-------------------------------------

module.exports = function(moduleName, options) {

    var config = {
        'class': null,
        directiveNames: {
            widthQuery: 'widthQuery',
            widthQueryClass: 'widthQueryClass'
        },
        modifiers: [
            ['small', 0, 767],
            ['medium', 768, 1023],
            ['large', 1024, 100000]
        ],
        pollingInterval: 50,
        maxPollingRetrys: 2,
        widthPollingListener: null,
        widthListener: null
    };

    if (options) {
        angular.merge(config, options);
    }

    var directiveNames = config.directiveNames;

    angular.module(moduleName).directive(directiveNames.widthQuery,
        function() {
            return directive(config);
        });

    widthQueryClass(moduleName, config);
};

//-------------------------------------
// Directive controller
//-------------------------------------

Controller.$inject = ['$scope', '$element', '$attrs', '$window', '$timeout'];

/*
 * @constructor
 */
function Controller($scope, $element, $attrs, $window, $timeout,
    config) {

    var m = this[MODEL] = new ControllerModel($scope, $element,
        $timeout, config);

    // Parse attribute values.
    var values = $attrs[config.directiveNames.widthQuery];
    if (values) {
        if (values[0] === '[') {
            var arr = $scope.$eval(values);
            m.config.class = arr[0];
            m.config.widthListener = arr[1];
        } else if (values[0] === '{') {
            angular.extend(m.config, $scope.$eval(values));
        } else {
            m.config.class = values;
        }
    }

    // Watch for window resizing.
    var window = angular.element($window);
    window.bind('resize', startPolling);

    // Watch for user event.
    $scope.$watch(function() {
        startPolling();
    }, function() {});

    // Clean Up.
    $scope.$on('$destroy', function() {
        m.cancelPendingNotification();
        window.unbind('resize', startPolling);
    });

    function startPolling() {
        m.startPolling();
    }
}

var proto = Controller.prototype;

/*
 * Add a listener to notify of class modifier changes.
 * Return the initial class modifier to use.
 */
proto.addModifierListener = function(listener) {
    var m = this[MODEL];
    m.classModifierListeners.push(listener);
    return m.initialModifier;
};

/*
 * Set the initial class modifier and listen for class modifier changes.
 */
proto.setInitialModifier = function(element) {
    var m = this[MODEL];
    var modifiedClass;
    var className = '';

    // Add class to element.
    if (m.config.class) {
        element.addClass(m.config.class);
        className = m.config.class + '-';
    }

    m.initialModifier = m.calculateModifier(m.element.offsetWidth);

    // Listen for class modifier changes.
    addModifiedClass(this.addModifierListener(addModifiedClass));

    function addModifiedClass(modifier) {
        if (modifiedClass) {
            element.removeClass(modifiedClass);
        }
        modifiedClass = className + modifier;
        element.addClass(modifiedClass);
    }
};

//-------------------------------------
// Directive controller model
//-------------------------------------

/*
 * @constructor
 */
function ControllerModel(scope, element, timeout, config) {
    this.scope = scope;
    this.element = element[0];
    this.timeout = timeout;
    this.classModifierListeners = [];
    this.pendingNotification = null;
    this.width = null;
    this.config = config;
    this.pollingRetry = 0;
}

var modelProto = ControllerModel.prototype;

/*
 * Cancel any pending class notification.
 */
modelProto.cancelPendingNotification = function() {
    if (this.pendingNotification) {
        this.timeout.cancel(this.pendingNotification);
    }
};

/*
 * When the width stops changing, apply the new class name.
 */
modelProto.startPolling = function() {
    var self = this;

    if (self.pendingNotification) {
        return;
    }

    // Update class modifiers while width is changing
    // rather than waiting till the end to give a more
    // fluid appearance.
    var width = self.element.offsetWidth;
    if (width !== self.width) {
        self.notifyModifierListeners(width);
        self.notifyWidthPollingListener(width);
        self.pollingRetry = 0;
        self.width = width;
    }

    self.pendingNotification = self.timeout(function() {
        var newWidth = self.element.offsetWidth;

        self.pendingNotification = null;

        // Width is still changing
        if (newWidth !== self.width) {
            self.startPolling();
        } else {
            if (self.pollingRetry === 0) {
                self.notifyWidthListener(newWidth);
            }

            // Try a couple more times to handle race conditions.
            self.pollingRetry++;
            if (self.pollingRetry <= self.config.maxPollingRetrys) {
                self.startPolling();
            } else {
                self.pollingRetry = 0;
            }
        }

    }, self.config.pollingInterval, false);
};

/*
 * Return the class modifer based upon the width.
 */
modelProto.calculateModifier = function(width) {
    var modifiers = this.config.modifiers;
    var length = modifiers.length;
    for (var i = 0; i < length; i++) {
        var modifier = modifiers[i];
        if (width >= modifier[1] && width <= modifier[2]) {
            return modifier[0];
        }
    }
    return '';
};

/*
 * Notify listeners to use the calculated class modifier.
 */
modelProto.notifyModifierListeners = function(width) {
    var classModifier = this.calculateModifier(width);

    var listeners = this.classModifierListeners;
    var length = listeners.length;
    for (var i = 0; i < length; i++) {
        listeners[i](classModifier);
    }
};

/*
 * Notify listener that the width has changed.
 */
modelProto.notifyWidthListener = function(width) {
    if (this.config.widthListener) {
        this.scope.$eval(this.config.widthListener, {
            width: width
        });
    }
};

/*
 * Notify listener that the width has changed during the polling period.
 */
modelProto.notifyWidthPollingListener = function(width) {
    if (this.config.widthPollingListener) {
        this.scope.$eval(this.config.widthPollingListener, {
            width: width
        });
    }
};

//-------------------------------------
// Directive
//-------------------------------------

function directive(config) {
    ProxyController.$inject = ['$scope', '$element', '$attrs',
        '$window', '$timeout'
    ];

    function ProxyController($scope, $element, $attrs, $window, $timeout) {
        Controller.call(this, $scope, $element,
            $attrs, $window, $timeout, angular.merge({}, config));
    }
    ProxyController.prototype = Controller.prototype;

    return {
        controller: ProxyController,
        link: {
            pre: function(scope, element, attrs, controller) {
                controller.setInitialModifier(element);
            }
        },
        require: config.directiveNames.widthQuery
    };

}
<commit_msg>Remove CRLF.<commit_after><|endoftext|>


<commit_before>exports.BattleStatuses = {
	frz: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target, 'frz');
		},
		duration: 2,
		onBeforeMovePriority: 2,
		onBeforeMove: function (pokemon, target, move) {
			if (move.thawsUser) {
				pokemon.cureStatus();
				return;
			}
			this.add('cant', pokemon, 'frz');
			return false;
		},
		onHit: function (target, source, move) {
			if (move.type === 'Fire' && move.category !== 'Status' || move.thawsUser) {
				target.cureStatus();
			}
		},
		onEnd: function (target) {
			this.battle.add('-curestatus', target, 'frz');
		}
	},
	lockedmove: {
		// Outrage, Thrash, Petal Dance...
		durationCallback: function () {
			return this.random(2, 4);
		},
		onResidual: function (target) {
			var move = this.getMove(target.lastMove);
			if (!move.self || move.self.volatileStatus !== 'lockedmove') {
				// don't lock, and bypass confusion for calming
				delete target.volatiles['lockedmove'];
			} else if (target.ability === 'owntempo') {
				// Own Tempo prevents locking
				delete target.volatiles['lockedmove'];
			}
		},
		onEnd: function (target) {
			this.add('-end', target, 'rampage');
			target.addVolatile('confusion');
		},
		onLockMove: function (pokemon) {
			return pokemon.lastMove;
		}
	},
	confusion: {
		// this is a volatile status
		onStart: function (target, source, sourceEffect) {
			var result = this.runEvent('TryConfusion', target, source, sourceEffect);
			if (!result) return result;
			this.add('-start', target, 'confusion');
			this.effectData.time = this.random(3, 4);
		},
		onEnd: function (target) {
			this.add('-end', target, 'confusion');
		},
		onBeforeMove: function (pokemon) {
			pokemon.volatiles.confusion.time--;
			if (!pokemon.volatiles.confusion.time) {
				pokemon.removeVolatile('confusion');
				return;
			}
			this.directDamage(this.getDamage(pokemon, pokemon, 30));
		}
	},

	// weather!

	raindance: {
		inherit: true,
		onBasePower: function (basePower, attacker, defender, move) {
			if (move.id === 'scald') {
				return;
			}
			if (move.type === 'Water') {
				this.debug('rain water boost');
				return basePower * 1.5;
			}
			if (move.type === 'Fire') {
				this.debug('rain fire suppress');
				return basePower * 0.5;
			}
		}
	},
	sunnyday: {
		inherit: true,
		onBasePower: function (basePower, attacker, defender, move) {
			if (move.id === 'scald') {
				return;
			}
			if (move.type === 'Fire') {
				this.debug('Sunny Day fire boost');
				return basePower * 1.5;
			}
			if (move.type === 'Water') {
				this.debug('Sunny Day water suppress');
				return basePower * 0.5;
			}
		}
	},

	// intrinsics!

	bidestall: {
		duration: 3
	},

	unown: {
		// Unown: Shadow Tag
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'shadowtag';
				pokemon.baseAbility = 'shadowtag';
			}
		},
		onModifyPokemon: function (pokemon) {
			if (pokemon.transformed) return;
			pokemon.setType(pokemon.hpType || 'Dark');
		}
	},
	bronzong: {
		// Bronzong: Heatproof
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'heatproof';
				pokemon.baseAbility = 'heatproof';
			}
		}
	},
	weezing: {
		// Weezing: Aftermath
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'aftermath';
				pokemon.baseAbility = 'aftermath';
			}
		}
	},
	flygon: {
		// Flygon: Compoundeyes
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'compoundeyes';
				pokemon.baseAbility = 'compoundeyes';
			}
		}
	},
	eelektross: {
		// Eelektross: Poison Heal
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'poisonheal';
				pokemon.baseAbility = 'poisonheal';
			}
		}
	},
	claydol: {
		// Claydol: Filter
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'filter';
				pokemon.baseAbility = 'filter';
			}
		}
	},
	gengar: {
		// Gengar: Cursed Body
		onImmunity: function (type, pokemon) {
			if (pokemon.template.id !== 'gengarmega' && type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'cursedbody';
				pokemon.baseAbility = 'cursedbody';
			}
		}
	},
	mismagius: {
		// Mismagius: Cursed Body
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'cursedbody';
				pokemon.baseAbility = 'cursedbody';
			}
		}
	},
	mesprit: {
		// Mesprit: Serene Grace
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'serenegrace';
				pokemon.baseAbility = 'serenegrace';
			}
		}
	},
	uxie: {
		// Uxie: Synchronize
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'synchronize';
				pokemon.baseAbility = 'synchronize';
			}
		}
	},
	azelf: {
		// Azelf: Steadfast
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'steadfast';
				pokemon.baseAbility = 'steadfast';
			}
		}
	},
	hydreigon: {
		// Hydreigon: Sheer Force
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'sheerforce';
				pokemon.baseAbility = 'sheerforce';
			}
		}
	},
	cryogonal: {
		// Cryogonal: infinite hail, Ice Body
		onModifyMove: function (move) {
			if (move.id === 'hail') {
				var weather = move.weather;
				move.weather = null;
				move.onHit = function (target, source) {
					this.setWeather(weather, source, this.getAbility('snowwarning'));
					this.weatherData.duration = 0;
				};
				move.target = 'self';
			}
		},
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'icebody';
				pokemon.baseAbility = 'icebody';
			}
		}
	},
	probopass: {
		// Probopass: infinite sand
		onModifyMove: function (move) {
			if (move.id === 'sandstorm') {
				var weather = move.weather;
				move.weather = null;
				move.onHit = function (target, source) {
					this.setWeather(weather, source, this.getAbility('sandstream'));
					this.weatherData.duration = 0;
				};
				move.target = 'self';
			}
		}
	},
	phione: {
		// Phione: infinite rain
		onModifyMove: function (move) {
			if (move.id === 'raindance') {
				var weather = move.weather;
				move.weather = null;
				move.onHit = function (target, source) {
					this.setWeather(weather, source, this.getAbility('drizzle'));
					this.weatherData.duration = 0;
				};
				move.target = 'self';
			}
		}
	}
};
<commit_msg>NEXT: Fix Weather affecting Steam Eruption's Base Power<commit_after>exports.BattleStatuses = {
	frz: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target, 'frz');
		},
		duration: 2,
		onBeforeMovePriority: 2,
		onBeforeMove: function (pokemon, target, move) {
			if (move.thawsUser) {
				pokemon.cureStatus();
				return;
			}
			this.add('cant', pokemon, 'frz');
			return false;
		},
		onHit: function (target, source, move) {
			if (move.type === 'Fire' && move.category !== 'Status' || move.thawsUser) {
				target.cureStatus();
			}
		},
		onEnd: function (target) {
			this.battle.add('-curestatus', target, 'frz');
		}
	},
	lockedmove: {
		// Outrage, Thrash, Petal Dance...
		durationCallback: function () {
			return this.random(2, 4);
		},
		onResidual: function (target) {
			var move = this.getMove(target.lastMove);
			if (!move.self || move.self.volatileStatus !== 'lockedmove') {
				// don't lock, and bypass confusion for calming
				delete target.volatiles['lockedmove'];
			} else if (target.ability === 'owntempo') {
				// Own Tempo prevents locking
				delete target.volatiles['lockedmove'];
			}
		},
		onEnd: function (target) {
			this.add('-end', target, 'rampage');
			target.addVolatile('confusion');
		},
		onLockMove: function (pokemon) {
			return pokemon.lastMove;
		}
	},
	confusion: {
		// this is a volatile status
		onStart: function (target, source, sourceEffect) {
			var result = this.runEvent('TryConfusion', target, source, sourceEffect);
			if (!result) return result;
			this.add('-start', target, 'confusion');
			this.effectData.time = this.random(3, 4);
		},
		onEnd: function (target) {
			this.add('-end', target, 'confusion');
		},
		onBeforeMove: function (pokemon) {
			pokemon.volatiles.confusion.time--;
			if (!pokemon.volatiles.confusion.time) {
				pokemon.removeVolatile('confusion');
				return;
			}
			this.directDamage(this.getDamage(pokemon, pokemon, 30));
		}
	},

	// weather!

	raindance: {
		inherit: true,
		onBasePower: function (basePower, attacker, defender, move) {
			if (move.id === 'scald' || move.id === 'steameruption') {
				return;
			}
			if (move.type === 'Water') {
				this.debug('rain water boost');
				return basePower * 1.5;
			}
			if (move.type === 'Fire') {
				this.debug('rain fire suppress');
				return basePower * 0.5;
			}
		}
	},
	sunnyday: {
		inherit: true,
		onBasePower: function (basePower, attacker, defender, move) {
			if (move.id === 'scald' || move.id === 'steameruption') {
				return;
			}
			if (move.type === 'Fire') {
				this.debug('Sunny Day fire boost');
				return basePower * 1.5;
			}
			if (move.type === 'Water') {
				this.debug('Sunny Day water suppress');
				return basePower * 0.5;
			}
		}
	},

	// intrinsics!

	bidestall: {
		duration: 3
	},

	unown: {
		// Unown: Shadow Tag
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'shadowtag';
				pokemon.baseAbility = 'shadowtag';
			}
		},
		onModifyPokemon: function (pokemon) {
			if (pokemon.transformed) return;
			pokemon.setType(pokemon.hpType || 'Dark');
		}
	},
	bronzong: {
		// Bronzong: Heatproof
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'heatproof';
				pokemon.baseAbility = 'heatproof';
			}
		}
	},
	weezing: {
		// Weezing: Aftermath
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'aftermath';
				pokemon.baseAbility = 'aftermath';
			}
		}
	},
	flygon: {
		// Flygon: Compoundeyes
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'compoundeyes';
				pokemon.baseAbility = 'compoundeyes';
			}
		}
	},
	eelektross: {
		// Eelektross: Poison Heal
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'poisonheal';
				pokemon.baseAbility = 'poisonheal';
			}
		}
	},
	claydol: {
		// Claydol: Filter
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'filter';
				pokemon.baseAbility = 'filter';
			}
		}
	},
	gengar: {
		// Gengar: Cursed Body
		onImmunity: function (type, pokemon) {
			if (pokemon.template.id !== 'gengarmega' && type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'cursedbody';
				pokemon.baseAbility = 'cursedbody';
			}
		}
	},
	mismagius: {
		// Mismagius: Cursed Body
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'cursedbody';
				pokemon.baseAbility = 'cursedbody';
			}
		}
	},
	mesprit: {
		// Mesprit: Serene Grace
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'serenegrace';
				pokemon.baseAbility = 'serenegrace';
			}
		}
	},
	uxie: {
		// Uxie: Synchronize
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'synchronize';
				pokemon.baseAbility = 'synchronize';
			}
		}
	},
	azelf: {
		// Azelf: Steadfast
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'steadfast';
				pokemon.baseAbility = 'steadfast';
			}
		}
	},
	hydreigon: {
		// Hydreigon: Sheer Force
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'sheerforce';
				pokemon.baseAbility = 'sheerforce';
			}
		}
	},
	cryogonal: {
		// Cryogonal: infinite hail, Ice Body
		onModifyMove: function (move) {
			if (move.id === 'hail') {
				var weather = move.weather;
				move.weather = null;
				move.onHit = function (target, source) {
					this.setWeather(weather, source, this.getAbility('snowwarning'));
					this.weatherData.duration = 0;
				};
				move.target = 'self';
			}
		},
		onImmunity: function (type, pokemon) {
			if (type === 'Ground' && pokemon.ignore['Ability'] !== 'A') return false;
		},
		onStart: function (pokemon) {
			if (pokemon.ability === 'levitate') {
				pokemon.ability = 'icebody';
				pokemon.baseAbility = 'icebody';
			}
		}
	},
	probopass: {
		// Probopass: infinite sand
		onModifyMove: function (move) {
			if (move.id === 'sandstorm') {
				var weather = move.weather;
				move.weather = null;
				move.onHit = function (target, source) {
					this.setWeather(weather, source, this.getAbility('sandstream'));
					this.weatherData.duration = 0;
				};
				move.target = 'self';
			}
		}
	},
	phione: {
		// Phione: infinite rain
		onModifyMove: function (move) {
			if (move.id === 'raindance') {
				var weather = move.weather;
				move.weather = null;
				move.onHit = function (target, source) {
					this.setWeather(weather, source, this.getAbility('drizzle'));
					this.weatherData.duration = 0;
				};
				move.target = 'self';
			}
		}
	}
};
<|endoftext|>


<commit_before>export function withEvents(Base = HTMLElement) {
  return class extends Base {
    connecting() {
      super.connecting && super.connecting();
      // Keep an object of listener types mapped to callback functions
      this._listeners = {};
    }

    disconnecting() {
      super.disconnecting && super.disconnecting();
      // Keep an object of listener types mapped to callback functions
      this._listeners = {};
    }

    /**
     * Register a new callback for the given event type
     *
     * @param {string} type
     * @param {Function} handler
     */
    on(type, handler) {
      if (typeof this._listeners[type] === 'undefined') {
        this._listeners[type] = [];
      }

      this._listeners[type].push(handler);

      return this;
    }

    /**
     * Unregister an existing callback for the given event type
     *
     * @param {string} type
     * @param {Function} handler
     */
    off(type, handler) {
      var index = this._listeners[type].indexOf(handler);

      if (index > -1) {
        this._listeners[type].splice(index, 1);
      }

      return this;
    }

    /**
     * Iterate over all registered handlers for given type and call them all with
     * the dialog element as first argument, event as second argument (if any).
     *
     * @access private
     * @param {string} type
     * @param {Event} event
     */
    _fire(type, ...props) {
      var listeners = this._listeners[type] || [];

      listeners.forEach(
        function(listener) {
          listener(this, ...props);
        }.bind(this),
      );
    }
  };
}
<commit_msg>fix: make sure all available prop data gets passed along to any listeners added<commit_after>export function withEvents(Base = HTMLElement) {
  return class extends Base {
    connecting() {
      super.connecting && super.connecting();
      // Keep an object of listener types mapped to callback functions
      this._listeners = {};
    }

    disconnecting() {
      super.disconnecting && super.disconnecting();
      // Keep an object of listener types mapped to callback functions
      this._listeners = {};
    }

    /**
     * Register a new callback for the given event type
     *
     * @param {string} type
     * @param {Function} handler
     */
    on(type, handler) {
      if (typeof this._listeners[type] === 'undefined') {
        this._listeners[type] = [];
      }

      this._listeners[type].push(handler);

      return this;
    }

    /**
     * Unregister an existing callback for the given event type
     *
     * @param {string} type
     * @param {Function} handler
     */
    off(type, handler) {
      var index = this._listeners[type].indexOf(handler);

      if (index > -1) {
        this._listeners[type].splice(index, 1);
      }

      return this;
    }

    /**
     * Iterate over all registered handlers for given type and call them all with
     * the dialog element as first argument, event as second argument (if any).
     *
     * @access private
     * @param {string} type
     * @param {Event} event
     */
    _fire(type, ...props) {
      var listeners = this._listeners[type] || [];

      listeners.forEach(
        function(listener) {
          listener(this, props);
        }.bind(this),
      );
    }
  };
}
<|endoftext|>


<commit_before>//This is a file for miscellaneous commands.

exports.commands = {
  hc: function(target, room, user) {
    return this.parse('/hotpatch chat')
  }
	pus: 'pmupperstaff',
	pmupperstaff: function(target, room, user) {
		if (!target) return this.sendReply('/pmupperstaff [message] - Sends a PM to every upper staff');
		if (!this.can('pban')) return false;
		Gold.pmUpperStaff(target, false, user.name);
	}
}
<commit_msg>Update alpha.js<commit_after>//This is a file for miscellaneous commands.

exports.commands = {
  hc: function(target, room, user) {
    return this.parse('/hotpatch chat')
  }
}
<|endoftext|>


<commit_before>/******************************
    Default shop setup
    for spacialgaze.psim.us
    coded by HoeenHero
    -----------------------
    THIS IS NOT THE SAME AS
    THE PRIVATE SHOP ON SG
******************************/
'use strict';

const fs = require('fs');
var allowThisShop = false; //Change to true to make these command work
var writeJSON = true;
SG.eShop = {};

function newItem (name, desc, price, isSSB) {
  this.name = name;
  this.id = toId(name);
  this.desc = Chat.escapeHTML(desc);
  this.price = Number(price);
  this.isSSB = Boolean(isSSB);
}

function writeShop() {
  if (!writeJSON) return false; //Prevent corruptions
  fs.writeFile('config/eShop.json', JSON.stringify(SG.eShop));
}

function shopDisplay() { //2px solid #070e96
  let output = '

Server Shop
';
  for (let i in SG.eShop) {
    if (!SG.eShop[i]) continue;
    output += '
<button name="send" value="/eshop buy ' + SG.eShop[i].id + '">' + SG.eShop[i].name + '</button>	' + SG.eShop[i].desc + '	' + SG.eShop[i].price + '
';
  }
  output += '

';
  return output;
}

try {
  fs.accessSync('config/eShop.json', fs.F_OK);
  let raw = JSON.parse(fs.readFileSync('config/eShop.json', 'utf8'));
  SG.eShop = raw;
} catch(e) {
  fs.writeFile('config/eShop.json', "{}", function(err) {
      if(err) {
          console.error('Error while loading eShop: ' + err);
          SG.eShop = {closed: true};
          writeJSON = false;
      } else {
        console.log("config/eShop.json not found, creating a new one...");
      }
  });
}

//Usage notification
try {
  fs.accessSync('chat-plugins/shop.js', fs.F_OK);
  if (allowThisShop) console.warn('Since the normal shop is up the eShop has been disabled.');
  allowThisShop = false;
} catch (e) {
  if (!allowThisShop) console.warn('Unable to find the normal shop, activating the eShop...');
  allowThisShop = true;
}

exports.commands =  {
  //shop: 'eshop', //Uncomment this if you want this to be able to be used using the /shop command
  eshop: {
    add: function (target, room, user, connection, cmd, message) {
      if (!this.can('roomowner')) return false;
      if (!allowThisShop) return this.errorReply('This shop is closed');
      if (SG.eShop.closed) return this.sendReply('An error closed the shop.');
      target = target.split(',');
      if (!target[2]) return this.parse('/eshop help');
      if (SG.eShop[toId(target[0])]) return this.errorReply(target[0] + ' is already in the shop.');
      if (isNaN(Number(target[2]))) return this.parse('/eshop help');
      var isSSB = false;
      if (toId(target[0]) === 'shiny' || toId(target[0]) === 'ffacustomsymbol' || toId(target[0]) === 'customability' || toId(target[0]) === 'customitem' || toId(target[0]) === 'custommove') isSSB = true;
      SG.eShop[toId(target[0])] = new newItem(target[0], target[1], target[2], isSSB);
      writeShop();
      return this.sendReply('The item ' + target[0] + ' was added.');
    },
    remove: function (target, room, user, connection, cmd, message) {
      if (!allowThisShop) return this.errorReply('This shop is closed');
      if (!this.can('roomowner')) return false;
      if (SG.eShop.closed) return this.sendReply('An error closed the shop.');
      if (!target) return this.parse('/eshop help');
      if (!SG.eShop[toId(target)]) return this.errorReply(target + ' is not in the shop.');
      delete SG.eShop[toId(target[0])];
      writeShop();
      return this.sendReply('The item ' + target[0] + ' was removed.');
    },
    buy: function (target, room, user, connection, cmd, message) {
      if (!allowThisShop) return this.errorReply('This shop is closed');
      if (!target) return this.parse('/eshop help buy');
      if (SG.eShop.closed) return this.sendReply('The shop is closed, come back later.');
      if (!SG.eShop[toId(target)]) return this.errorReply('Item ' + target + ' not found.');
      let item = SG.eShop[toId(target)];
      Economy.readMoney(user.userid, userMoney => {
        if (item.price > userMoney) return this.errorReply('You need ' + (item.price - userMoney) + ' more ' + ((item.price - userMoney) === 1 ? global.currencyName : global.currenyPlural) + ' to buy this.');
        if (item.isSSB && !SG.ssb[user.userid]) return this.sendReply('You need to run /ssb edit at least once before you can buy this.');
        if (item.isSSB) {
          //handle SSB pre-buy events
          switch (item.id) {
            case 'shiny':
              if (SG.ssb[user.userid].canShiny) return this.sendReply('You already own this.');
              break;
            case 'ffacustomsymbol':
              if (SG.ssb[user.userid].cSymbol) return this.sendReply('You already own this.');
              if (user.isStaff || user.group === '+') {
                //give free
                SG.ssb[user.userid].cSymbol = true;
                return this.sendReply('Because you have global auth you have been given this for free!');
              }
              break;
            case 'customitem':
              if (SG.ssb[user.userid].bought.cItem) return this.sendReply('You already own this.');
              break;
            case 'customability':
              if (SG.ssb[user.userid].bought.cAbility) return this.sendReply('You already own this.');
              break;
            case 'custommove':
              if (SG.ssb[user.userid].bought.cMove) return this.sendReply('You already own this.');
              break;
            default:
              //Unhandled
          }
        }
        Economy.writeMoney(user.userid, item.price * -1, () => {
					Economy.readMoney(user.userid, amount => {
						Economy.logTransaction(user.name + " has purchased a " + item.name + " for " + item.price + " " + (item.price === 1 ? global.currencyName : global.currenyPlural)  + ". They now have " + amount + " " + (userMoney === 1 ? global.currencyName : global.currenyPlural));
            switch (item.id) {
              case 'customsymbol':
                user.canCustomSymbol = true;
                break;
              case 'shiny':
                SG.ssb[user.userid].canShiny = true;
                break;
              case 'ffacustomsymbol':
                SG.ssb[user.userid].cSymbol = true;
                break;
              case 'customitem':
                SG.ssb[user.userid].bought.cItem = true;
                break;
              case 'customability':
                SG.ssb[user.userid].bought.cAbility = true;
                break;
              case 'custommove':
                SG.ssb[user.userid].bought.cMove = true;
                break;
              default:
                SG.messageSeniorStaff(user.name + ' has purchased a ' + item.name + '.');
            }
						user.sendTo(room, "|uhtmlchange|eshop" + user.userid + "|

Server Shop

You have purchased a " + item.name + ". " + (item.id === 'customsymbol' ? "You may now use /customsymbol [symbol] to change your symbol." : "Upper staff have been notified of your purchase and will contact you shortly.") + "

<button name='send' value='/eshop reopen'>Return to Shop</button>

");
					});
				});
      });
    },
    help: function (target, room, user, connection, cmd, message) {
      let reply = 'Shop commands
';
      reply += '/eshop - Load the shop screen.
';
      reply += '/eshop buy [item] - Buy an item from the shop.
';
      if (user.can('roomowner')) {
        reply += 'Administrative shop commands:
';
        reply += '/eshop add [item name], [description], [price], (is a SSBFFA item) - Adds a item to the shop.
';
        reply += 'Valid SSBFFA items are: shiny, ffacustommove, customitem, customability, custommove.
';
        reply += '/eshop remove [item] - removes a item from the shop.
';
      }
      return this.sendReplyBox(reply);
    },
    reopen: '',
    '': function (target, room, user, connection, cmd, message) {
      if (!allowThisShop) return this.errorReply('This shop is closed');
      if (cmd === 'reopen') return user.sendTo(room, '|uhtmlchange|eshop' + user.userid + '|' + shopDisplay());
      return user.sendTo(room, '|uhtml|eshop' + user.userid + '|' + shopDisplay());
    }
  }
}
<commit_msg>Fix eshop bug<commit_after>/******************************
    Default shop setup
    for spacialgaze.psim.us
    coded by HoeenHero
    -----------------------
    THIS IS NOT THE SAME AS
    THE PRIVATE SHOP ON SG
******************************/
'use strict';

const fs = require('fs');
var allowThisShop = false; //Change to true to make these command work
var writeJSON = true;
SG.eShop = {};

function newItem (name, desc, price, isSSB) {
  this.name = name;
  this.id = toId(name);
  this.desc = Chat.escapeHTML(desc);
  this.price = Number(price);
  this.isSSB = Boolean(isSSB);
}

function writeShop() {
  if (!writeJSON) return false; //Prevent corruptions
  fs.writeFile('config/eShop.json', JSON.stringify(SG.eShop));
}

function shopDisplay() { //2px solid #070e96
  let output = '

Server Shop
';
  for (let i in SG.eShop) {
    if (!SG.eShop[i]) continue;
    output += '
<button name="send" value="/eshop buy ' + SG.eShop[i].id + '">' + SG.eShop[i].name + '</button>	' + SG.eShop[i].desc + '	' + SG.eShop[i].price + '
';
  }
  output += '

';
  return output;
}

try {
  fs.accessSync('config/eShop.json', fs.F_OK);
  let raw = JSON.parse(fs.readFileSync('config/eShop.json', 'utf8'));
  SG.eShop = raw;
} catch(e) {
  fs.writeFile('config/eShop.json', "{}", function(err) {
      if(err) {
          console.error('Error while loading eShop: ' + err);
          SG.eShop = {closed: true};
          writeJSON = false;
      } else {
        console.log("config/eShop.json not found, creating a new one...");
      }
  });
}

//Usage notification
try {
  fs.accessSync('chat-plugins/shop.js', fs.F_OK);
  if (allowThisShop) console.warn('Since the normal shop is up the eShop has been disabled.');
  allowThisShop = false;
} catch (e) {
  if (!allowThisShop) console.warn('Unable to find the normal shop, activating the eShop...');
  allowThisShop = true;
}

exports.commands =  {
  //shop: 'eshop', //Uncomment this if you want this to be able to be used using the /shop command
  eshop: {
    add: function (target, room, user, connection, cmd, message) {
      if (!this.can('roomowner')) return false;
      if (!allowThisShop) return this.errorReply('This shop is closed');
      if (SG.eShop.closed) return this.sendReply('An error closed the shop.');
      target = target.split(',');
      if (!target[2]) return this.parse('/eshop help');
      if (SG.eShop[toId(target[0])]) return this.errorReply(target[0] + ' is already in the shop.');
      if (isNaN(Number(target[2]))) return this.parse('/eshop help');
      var isSSB = false;
      if (toId(target[0]) === 'shiny' || toId(target[0]) === 'ffacustomsymbol' || toId(target[0]) === 'customability' || toId(target[0]) === 'customitem' || toId(target[0]) === 'custommove') isSSB = true;
      SG.eShop[toId(target[0])] = new newItem(target[0], target[1], target[2], isSSB);
      writeShop();
      return this.sendReply('The item ' + target[0] + ' was added.');
    },
    remove: function (target, room, user, connection, cmd, message) {
      if (!allowThisShop) return this.errorReply('This shop is closed');
      if (!this.can('roomowner')) return false;
      if (SG.eShop.closed) return this.sendReply('An error closed the shop.');
      if (!target) return this.parse('/eshop help');
      if (!SG.eShop[toId(target)]) return this.errorReply(target + ' is not in the shop.');
      delete SG.eShop[toId(target)];
      writeShop();
      return this.sendReply('The item ' + target + ' was removed.');
    },
    buy: function (target, room, user, connection, cmd, message) {
      if (!allowThisShop) return this.errorReply('This shop is closed');
      if (!target) return this.parse('/eshop help buy');
      if (SG.eShop.closed) return this.sendReply('The shop is closed, come back later.');
      if (!SG.eShop[toId(target)]) return this.errorReply('Item ' + target + ' not found.');
      let item = SG.eShop[toId(target)];
      Economy.readMoney(user.userid, userMoney => {
        if (item.price > userMoney) return this.errorReply('You need ' + (item.price - userMoney) + ' more ' + ((item.price - userMoney) === 1 ? global.currencyName : global.currenyPlural) + ' to buy this.');
        if (item.isSSB && !SG.ssb[user.userid]) return this.sendReply('You need to run /ssb edit at least once before you can buy this.');
        if (item.isSSB) {
          //handle SSB pre-buy events
          switch (item.id) {
            case 'shiny':
              if (SG.ssb[user.userid].canShiny) return this.sendReply('You already own this.');
              break;
            case 'ffacustomsymbol':
              if (SG.ssb[user.userid].cSymbol) return this.sendReply('You already own this.');
              if (user.isStaff || user.group === '+') {
                //give free
                SG.ssb[user.userid].cSymbol = true;
                return this.sendReply('Because you have global auth you have been given this for free!');
              }
              break;
            case 'customitem':
              if (SG.ssb[user.userid].bought.cItem) return this.sendReply('You already own this.');
              break;
            case 'customability':
              if (SG.ssb[user.userid].bought.cAbility) return this.sendReply('You already own this.');
              break;
            case 'custommove':
              if (SG.ssb[user.userid].bought.cMove) return this.sendReply('You already own this.');
              break;
            default:
              //Unhandled
          }
        }
        Economy.writeMoney(user.userid, item.price * -1, () => {
					Economy.readMoney(user.userid, amount => {
						Economy.logTransaction(user.name + " has purchased a " + item.name + " for " + item.price + " " + (item.price === 1 ? global.currencyName : global.currenyPlural)  + ". They now have " + amount + " " + (userMoney === 1 ? global.currencyName : global.currenyPlural));
            switch (item.id) {
              case 'customsymbol':
                user.canCustomSymbol = true;
                break;
              case 'shiny':
                SG.ssb[user.userid].canShiny = true;
                break;
              case 'ffacustomsymbol':
                SG.ssb[user.userid].cSymbol = true;
                break;
              case 'customitem':
                SG.ssb[user.userid].bought.cItem = true;
                break;
              case 'customability':
                SG.ssb[user.userid].bought.cAbility = true;
                break;
              case 'custommove':
                SG.ssb[user.userid].bought.cMove = true;
                break;
              default:
                SG.messageSeniorStaff(user.name + ' has purchased a ' + item.name + '.');
            }
						user.sendTo(room, "|uhtmlchange|eshop" + user.userid + "|

Server Shop

You have purchased a " + item.name + ". " + (item.id === 'customsymbol' ? "You may now use /customsymbol [symbol] to change your symbol." : "Upper staff have been notified of your purchase and will contact you shortly.") + "

<button name='send' value='/eshop reopen'>Return to Shop</button>

");
					});
				});
      });
    },
    help: function (target, room, user, connection, cmd, message) {
      let reply = 'Shop commands
';
      reply += '/eshop - Load the shop screen.
';
      reply += '/eshop buy [item] - Buy an item from the shop.
';
      if (user.can('roomowner')) {
        reply += 'Administrative shop commands:
';
        reply += '/eshop add [item name], [description], [price], (is a SSBFFA item) - Adds a item to the shop.
';
        reply += 'Valid SSBFFA items are: shiny, ffacustommove, customitem, customability, custommove.
';
        reply += '/eshop remove [item] - removes a item from the shop.
';
      }
      return this.sendReplyBox(reply);
    },
    reopen: '',
    '': function (target, room, user, connection, cmd, message) {
      if (!allowThisShop) return this.errorReply('This shop is closed');
      if (cmd === 'reopen') return user.sendTo(room, '|uhtmlchange|eshop' + user.userid + '|' + shopDisplay());
      return user.sendTo(room, '|uhtml|eshop' + user.userid + '|' + shopDisplay());
    }
  }
}
<|endoftext|>


<commit_before>/**
@module ember
@submodule ember-routing
*/

var get = Ember.get, set = Ember.set;

Ember.View.reopen({
  init: function() {
    set(this, '_outlets', {});
    this._super();
  },

  connectOutlet: function(outletName, view) {
    if (this._pendingDisconnections) {
      delete this._pendingDisconnections[outletName];
    }

    if (this._hasEquivalentView(outletName, view)) {
      return;
    }

    var outlets = get(this, '_outlets'),
        container = get(this, 'container'),
        router = container && container.lookup('router:main'),
        renderedName = get(view, 'renderedName');

    set(outlets, outletName, view);

    if (router && renderedName) {
      router._connectActiveView(renderedName, view);
    }
  },

  _hasEquivalentView: function(outletName, view) {
    var existingView = get(this, '_outlets.'+outletName);
    return existingView &&
      existingView.prototype === view.prototype &&
      existingView.get('template') === view.get('template') &&
      existingView.get('context') === view.get('context');
  },

  disconnectOutlet: function(outletName) {
    if (!this._pendingDisconnections) {
      this._pendingDisconnections = {};
    }
    this._pendingDisconnections[outletName] = true;
    Ember.run.once(this, '_finishDisconnections');
  },

  _finishDisconnections: function() {
    var outlets = get(this, '_outlets');
    for (var outletName in this._pendingDisconnections) {
      set(outlets, outletName, null);
    }
  }
});
<commit_msg>Clean up pendingDisconnections propertly<commit_after>/**
@module ember
@submodule ember-routing
*/

var get = Ember.get, set = Ember.set;

Ember.View.reopen({
  init: function() {
    set(this, '_outlets', {});
    this._super();
  },

  connectOutlet: function(outletName, view) {
    if (this._pendingDisconnections) {
      delete this._pendingDisconnections[outletName];
    }

    if (this._hasEquivalentView(outletName, view)) {
      return;
    }

    var outlets = get(this, '_outlets'),
        container = get(this, 'container'),
        router = container && container.lookup('router:main'),
        renderedName = get(view, 'renderedName');

    set(outlets, outletName, view);

    if (router && renderedName) {
      router._connectActiveView(renderedName, view);
    }
  },

  _hasEquivalentView: function(outletName, view) {
    var existingView = get(this, '_outlets.'+outletName);
    return existingView &&
      existingView.prototype === view.prototype &&
      existingView.get('template') === view.get('template') &&
      existingView.get('context') === view.get('context');
  },

  disconnectOutlet: function(outletName) {
    if (!this._pendingDisconnections) {
      this._pendingDisconnections = {};
    }
    this._pendingDisconnections[outletName] = true;
    Ember.run.once(this, '_finishDisconnections');
  },

  _finishDisconnections: function() {
    var outlets = get(this, '_outlets');
    var pendingDisconnections = this._pendingDisconnections;
    this._pendingDisconnections = null;

    for (var outletName in pendingDisconnections) {
      set(outlets, outletName, null);
    }
  }
});
<|endoftext|>


<commit_before>/**
 * Copyright 2014-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule warning
 */

'use strict';

var emptyFunction = require('emptyFunction');

/**
 * Similar to invariant but only logs a warning if the condition is not met.
 * This can be used to log issues in development environments in critical
 * paths. Removing the logging code for production environments will keep the
 * same logic and follow the same code paths.
 */

var warning = emptyFunction;

if (__DEV__) {
  warning = function(condition, format, ...args) {
    if (format === undefined) {
      throw new Error(
        '`warning(condition, format, ...args)` requires a warning ' +
        'message argument'
      );
    }

    if (!condition) {
      var argIndex = 0;
      var message = 'Warning: ' + format.replace(/%s/g, () => args[argIndex++]);
      if (typeof console !== 'undefined') {
        console.error(message);
      }
      try {
        // --- Welcome to debugging React ---
        // This error was thrown as a convenience so that you can use this stack
        // to find the callsite that caused this warning to fire.
        throw new Error(message);
      } catch (x) {}
    }
  };
}

module.exports = warning;
<commit_msg>Extract try/catch into a separate function to prevent deopt (#171)<commit_after>/**
 * Copyright 2014-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule warning
 */

'use strict';

var emptyFunction = require('emptyFunction');

/**
 * Similar to invariant but only logs a warning if the condition is not met.
 * This can be used to log issues in development environments in critical
 * paths. Removing the logging code for production environments will keep the
 * same logic and follow the same code paths.
 */

var warning = emptyFunction;

if (__DEV__) {
  function printWarning(format, ...args) {
    var argIndex = 0;
    var message = 'Warning: ' + format.replace(/%s/g, () => args[argIndex++]);
    if (typeof console !== 'undefined') {
      console.error(message);
    }
    try {
      // --- Welcome to debugging React ---
      // This error was thrown as a convenience so that you can use this stack
      // to find the callsite that caused this warning to fire.
      throw new Error(message);
    } catch (x) {}
  }

  warning = function(condition, format, ...args) {
    if (format === undefined) {
      throw new Error(
        '`warning(condition, format, ...args)` requires a warning ' +
        'message argument'
      );
    }
    if (!condition) {
      printWarning(format, ...args);
    }
  };
}

module.exports = warning;
<|endoftext|>