<commit_before>import Controller from '@ember/controller';
import {action} from '@ember/object';
import {currencies, getCurrencyOptions, getSymbol} from 'ghost-admin/utils/currency';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

const CURRENCIES = currencies.map((currency) => {
    return {
        value: currency.isoCode.toLowerCase(),
        label: `${currency.isoCode} - ${currency.name}`,
        isoCode: currency.isoCode
    };
});

export default class MembersAccessController extends Controller {
    @service config;
    @service membersUtils;
    @service settings;
    @service store;

    @tracked showLeavePortalModal = false;
    @tracked showLeaveRouteModal = false;
    @tracked showPortalSettings = false;
    @tracked showStripeConnect = false;

    @tracked product = null;
    @tracked stripePrices = [];
    @tracked paidSignupRedirect;
    @tracked freeSignupRedirect;
    @tracked stripeMonthlyAmount = 5;
    @tracked stripeYearlyAmount = 50;
    @tracked currency = 'usd';
    @tracked stripePlanError = '';

    @tracked portalPreviewUrl = '';

    queryParams = ['showPortalSettings'];

    get allCurrencies() {
        return getCurrencyOptions();
    }

    get siteUrl() {
        return this.config.get('blogUrl');
    }

    get selectedCurrency() {
        return CURRENCIES.findBy('value', this.currency);
    }

    leaveRoute(transition) {
        if (this.settings.get('hasDirtyAttributes')) {
            transition.abort();
            this.leaveSettingsTransition = transition;
            this.showLeaveRouteModal = true;
        }
    }

    _validateSignupRedirect(url, type) {
        const siteUrl = this.config.get('blogUrl');
        let errMessage = `Please enter a valid URL`;
        this.settings.get('errors').remove(type);
        this.settings.get('hasValidated').removeObject(type);

        if (url === null) {
            this.settings.get('errors').add(type, errMessage);
            this.settings.get('hasValidated').pushObject(type);
            return false;
        }

        if (url === undefined) {
            // Not initialised
            return;
        }

        if (url.href.startsWith(siteUrl)) {
            const path = url.href.replace(siteUrl, '');
            this.settings.set(type, path);
        } else {
            this.settings.set(type, url.href);
        }
    }

    @action
    setStripePlansCurrency(event) {
        const newCurrency = event.value;
        this.currency = newCurrency;
    }

    @action
    setPaidSignupRedirect(url) {
        this.paidSignupRedirect = url;
    }

    @action
    setFreeSignupRedirect(url) {
        this.freeSignupRedirect = url;
    }

    @action
    validatePaidSignupRedirect() {
        return this._validateSignupRedirect(this.paidSignupRedirect, 'membersPaidSignupRedirect');
    }

    @action
    validateFreeSignupRedirect() {
        return this._validateSignupRedirect(this.freeSignupRedirect, 'membersFreeSignupRedirect');
    }

    @action
    validateStripePlans() {
        this.stripePlanError = undefined;

        try {
            const yearlyAmount = this.stripeYearlyAmount;
            const monthlyAmount = this.stripeMonthlyAmount;
            const symbol = getSymbol(this.currency);
            if (!yearlyAmount || yearlyAmount < 1 || !monthlyAmount || monthlyAmount < 1) {
                throw new TypeError(`Subscription amount must be at least ${symbol}1.00`);
            }

            this.updatePortalPreview();
        } catch (err) {
            this.stripePlanError = err.message;
        }
    }

    @action
    closeStripeConnect() {
        this.showStripeConnect = false;
    }

    @action
    openPortalSettings() {
        this.saveSettingsTask.perform();
        this.showPortalSettings = true;
    }

    @action
    closePortalSettings() {
        const changedAttributes = this.settings.changedAttributes();
        if (changedAttributes && Object.keys(changedAttributes).length > 0) {
            this.showLeavePortalModal = true;
        } else {
            this.showPortalSettings = false;
            this.updatePortalPreview();
        }
    }

    @action
    async confirmClosePortalSettings() {
        this.settings.rollbackAttributes();
        this.showPortalSettings = false;
        this.showLeavePortalModal = false;
        this.updatePortalPreview();
    }

    @action
    cancelClosePortalSettings() {
        this.showLeavePortalModal = false;
    }

    @action
    async confirmLeave() {
        this.settings.rollbackAttributes();
        this.leaveSettingsTransition.retry();
    }

    @action
    cancelLeave() {
        this.showLeaveRouteModal = false;
        this.leaveSettingsTransition = null;
    }

    @action
    updatePortalPreview() {
        // TODO: can these be worked out from settings in membersUtils?
        const monthlyPrice = this.stripeMonthlyAmount;
        const yearlyPrice = this.stripeYearlyAmount;

        this.portalPreviewUrl = this.membersUtils.getPortalPreviewUrl({
            button: false,
            monthlyPrice,
            yearlyPrice
        });

        this.resizePortalPreviewTask.perform();
    }

    @action
    portalPreviewLoaded(event) {
        this.portalPreviewIframe = event.target;
        this.resizePortalPreviewTask.perform();
    }

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

    @task({restartable: true})
    *resizePortalPreviewTask() {
        if (this.portalPreviewIframe && this.portalPreviewIframe.contentWindow) {
            yield timeout(100); // give time for portal to re-render

            const portalIframe = this.portalPreviewIframe.contentWindow.document.querySelector('#ghost-portal-root iframe');
            if (!portalIframe) {
                return;
            }

            const portalContainer = portalIframe.contentWindow.document.querySelector('.gh-portal-popup-container');
            if (!portalContainer) {
                return;
            }

            const height = portalContainer.clientHeight;
            this.portalPreviewIframe.parentNode.style.height = `${height}px`;
        }
    }

    @action
    setup() {
        this.fetchDefaultProduct.perform();
        this.updatePortalPreview();
    }

    async saveProduct() {
        if (this.product) {
            const stripePrices = this.product.stripePrices || [];
            const monthlyAmount = this.stripeMonthlyAmount * 100;
            const yearlyAmount = this.stripeYearlyAmount * 100;
            const getActivePrice = (prices, type, amount) => {
                return prices.find((price) => {
                    return (
                        price.active && price.amount === amount && price.type === 'recurring' &&
                        price.interval === type && price.currency.toLowerCase() === this.currency.toLowerCase()
                    );
                });
            };
            const monthlyPrice = getActivePrice(stripePrices, 'month', monthlyAmount);
            const yearlyPrice = getActivePrice(stripePrices, 'year', yearlyAmount);

            if (!monthlyPrice) {
                stripePrices.push(
                    {
                        nickname: 'Monthly',
                        amount: monthlyAmount,
                        active: 1,
                        currency: this.currency,
                        interval: 'month',
                        type: 'recurring'
                    }
                );
            }
            if (!yearlyPrice) {
                stripePrices.push(
                    {
                        nickname: 'Yearly',
                        amount: this.stripeYearlyAmount * 100,
                        active: 1,
                        currency: this.currency,
                        interval: 'year',
                        type: 'recurring'
                    }
                );
            }
            if (monthlyPrice && yearlyPrice) {
                this.updatePortalPlans(monthlyPrice.id, yearlyPrice.id);
                this.settings.set('membersMonthlyPriceId', monthlyPrice.id);
                this.settings.set('membersYearlyPriceId', yearlyPrice.id);
                return this.product;
            } else {
                this.product.set('stripePrices', stripePrices);
                const savedProduct = await this.product.save();
                const updatedStripePrices = savedProduct.stripePrices || [];
                const updatedMonthlyPrice = getActivePrice(updatedStripePrices, 'month', monthlyAmount);
                const updatedYearlyPrice = getActivePrice(updatedStripePrices, 'year', yearlyAmount);
                this.updatePortalPlans(updatedMonthlyPrice.id, updatedYearlyPrice.id);
                this.settings.set('membersMonthlyPriceId', updatedMonthlyPrice.id);
                this.settings.set('membersYearlyPriceId', updatedYearlyPrice.id);
                return savedProduct;
            }
        }
    }

    updatePortalPlans(monthlyPriceId, yearlyPriceId) {
        let portalPlans = this.settings.get('portalPlans') || [];
        const currentMontlyPriceId = this.settings.get('membersMonthlyPriceId');
        const currentYearlyPriceId = this.settings.get('membersYearlyPriceId');
        if (portalPlans.includes(currentMontlyPriceId)) {
            portalPlans = portalPlans.filter(d => d.id !== currentMontlyPriceId);
            portalPlans.push(monthlyPriceId);
        }

        if (portalPlans.includes(currentYearlyPriceId)) {
            portalPlans = portalPlans.filter(d => d.id !== currentYearlyPriceId);
            portalPlans.push(yearlyPriceId);
        }
        this.settings.set('portalPlans', portalPlans);
    }

    getPrice(prices, type) {
        const monthlyPriceId = this.settings.get('membersMonthlyPriceId');
        const yearlyPriceId = this.settings.get('membersYearlyPriceId');

        if (type === 'monthly') {
            return (
                prices.find(price => price.id === monthlyPriceId) ||
                prices.find(price => price.nickname === 'Monthly') ||
                prices.find(price => price.interval === 'month')
            );
        }

        if (type === 'yearly') {
            return (
                prices.find(price => price.id === yearlyPriceId) ||
                prices.find(price => price.nickname === 'Yearly') ||
                prices.find(price => price.interval === 'year')
            );
        }
        return null;
    }

    @task({drop: true})
    *fetchDefaultProduct() {
        const products = yield this.store.query('product', {include: 'stripe_prices'});
        this.product = products.firstObject;
        this.stripePrices = [];
        if (this.product) {
            this.stripePrices = this.product.get('stripePrices') || [];
            const activePrices = this.stripePrices.filter(price => !!price.active);
            const monthlyPrice = this.getPrice(activePrices, 'monthly');
            const yearlyPrice = this.getPrice(activePrices, 'yearly');
            if (monthlyPrice && monthlyPrice.amount) {
                this.stripeMonthlyAmount = (monthlyPrice.amount / 100);
                this.currency = monthlyPrice.currency;
            }
            if (yearlyPrice && yearlyPrice.amount) {
                this.stripeYearlyAmount = (yearlyPrice.amount / 100);
            }
        }
    }

    @task({drop: true})
    *saveSettingsTask() {
        yield this.validateStripePlans();

        if (this.stripePlanError) {
            return;
        }

        if (this.settings.get('errors').length !== 0) {
            return;
        }

        yield this.saveProduct();
        const result = yield this.settings.save();

        this.updatePortalPreview();

        return result;
    }

    reset() {
        this.showLeaveRouteModal = false;
        this.showLeavePortalModal = false;
        this.showPortalSettings = false;
    }
}
<commit_msg>Updated portal preview to update on price change<commit_after>import Controller from '@ember/controller';
import {action} from '@ember/object';
import {currencies, getCurrencyOptions, getSymbol} from 'ghost-admin/utils/currency';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

const CURRENCIES = currencies.map((currency) => {
    return {
        value: currency.isoCode.toLowerCase(),
        label: `${currency.isoCode} - ${currency.name}`,
        isoCode: currency.isoCode
    };
});

export default class MembersAccessController extends Controller {
    @service config;
    @service membersUtils;
    @service settings;
    @service store;

    @tracked showLeavePortalModal = false;
    @tracked showLeaveRouteModal = false;
    @tracked showPortalSettings = false;
    @tracked showStripeConnect = false;

    @tracked product = null;
    @tracked stripePrices = [];
    @tracked paidSignupRedirect;
    @tracked freeSignupRedirect;
    @tracked stripeMonthlyAmount = 5;
    @tracked stripeYearlyAmount = 50;
    @tracked currency = 'usd';
    @tracked stripePlanError = '';

    @tracked portalPreviewUrl = '';

    queryParams = ['showPortalSettings'];

    get allCurrencies() {
        return getCurrencyOptions();
    }

    get siteUrl() {
        return this.config.get('blogUrl');
    }

    get selectedCurrency() {
        return CURRENCIES.findBy('value', this.currency);
    }

    leaveRoute(transition) {
        if (this.settings.get('hasDirtyAttributes')) {
            transition.abort();
            this.leaveSettingsTransition = transition;
            this.showLeaveRouteModal = true;
        }
    }

    _validateSignupRedirect(url, type) {
        const siteUrl = this.config.get('blogUrl');
        let errMessage = `Please enter a valid URL`;
        this.settings.get('errors').remove(type);
        this.settings.get('hasValidated').removeObject(type);

        if (url === null) {
            this.settings.get('errors').add(type, errMessage);
            this.settings.get('hasValidated').pushObject(type);
            return false;
        }

        if (url === undefined) {
            // Not initialised
            return;
        }

        if (url.href.startsWith(siteUrl)) {
            const path = url.href.replace(siteUrl, '');
            this.settings.set(type, path);
        } else {
            this.settings.set(type, url.href);
        }
    }

    @action
    setStripePlansCurrency(event) {
        const newCurrency = event.value;
        this.currency = newCurrency;
    }

    @action
    setPaidSignupRedirect(url) {
        this.paidSignupRedirect = url;
    }

    @action
    setFreeSignupRedirect(url) {
        this.freeSignupRedirect = url;
    }

    @action
    validatePaidSignupRedirect() {
        return this._validateSignupRedirect(this.paidSignupRedirect, 'membersPaidSignupRedirect');
    }

    @action
    validateFreeSignupRedirect() {
        return this._validateSignupRedirect(this.freeSignupRedirect, 'membersFreeSignupRedirect');
    }

    @action
    validateStripePlans() {
        this.stripePlanError = undefined;

        try {
            const yearlyAmount = this.stripeYearlyAmount;
            const monthlyAmount = this.stripeMonthlyAmount;
            const symbol = getSymbol(this.currency);
            if (!yearlyAmount || yearlyAmount < 1 || !monthlyAmount || monthlyAmount < 1) {
                throw new TypeError(`Subscription amount must be at least ${symbol}1.00`);
            }

            this.updatePortalPreview();
        } catch (err) {
            this.stripePlanError = err.message;
        }
    }

    @action
    closeStripeConnect() {
        this.showStripeConnect = false;
    }

    @action
    openPortalSettings() {
        this.saveSettingsTask.perform();
        this.showPortalSettings = true;
    }

    @action
    closePortalSettings() {
        const changedAttributes = this.settings.changedAttributes();
        if (changedAttributes && Object.keys(changedAttributes).length > 0) {
            this.showLeavePortalModal = true;
        } else {
            this.showPortalSettings = false;
            this.updatePortalPreview();
        }
    }

    @action
    async confirmClosePortalSettings() {
        this.settings.rollbackAttributes();
        this.showPortalSettings = false;
        this.showLeavePortalModal = false;
        this.updatePortalPreview();
    }

    @action
    cancelClosePortalSettings() {
        this.showLeavePortalModal = false;
    }

    @action
    async confirmLeave() {
        this.settings.rollbackAttributes();
        this.leaveSettingsTransition.retry();
    }

    @action
    cancelLeave() {
        this.showLeaveRouteModal = false;
        this.leaveSettingsTransition = null;
    }

    @action
    updatePortalPreview() {
        // TODO: can these be worked out from settings in membersUtils?
        const monthlyPrice = this.stripeMonthlyAmount * 100;
        const yearlyPrice = this.stripeYearlyAmount * 100;
        let portalPlans = this.settings.get('portalPlans') || [];
        const currentMontlyPriceId = this.settings.get('membersMonthlyPriceId');
        const currentYearlyPriceId = this.settings.get('membersYearlyPriceId');
        let isMonthlyChecked = false;
        let isYearlyChecked = false;
        if (portalPlans.includes(currentMontlyPriceId)) {
            isMonthlyChecked = true;
        }

        if (portalPlans.includes(currentYearlyPriceId)) {
            isYearlyChecked = true;
        }

        this.portalPreviewUrl = this.membersUtils.getPortalPreviewUrl({
            button: false,
            monthlyPrice,
            yearlyPrice,
            currency: this.currency,
            isMonthlyChecked,
            isYearlyChecked,
            portalPlans: null
        });

        this.resizePortalPreviewTask.perform();
    }

    @action
    portalPreviewLoaded(event) {
        this.portalPreviewIframe = event.target;
        this.resizePortalPreviewTask.perform();
    }

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

    @task({restartable: true})
    *resizePortalPreviewTask() {
        if (this.portalPreviewIframe && this.portalPreviewIframe.contentWindow) {
            yield timeout(100); // give time for portal to re-render

            const portalIframe = this.portalPreviewIframe.contentWindow.document.querySelector('#ghost-portal-root iframe');
            if (!portalIframe) {
                return;
            }

            const portalContainer = portalIframe.contentWindow.document.querySelector('.gh-portal-popup-container');
            if (!portalContainer) {
                return;
            }

            const height = portalContainer.clientHeight;
            this.portalPreviewIframe.parentNode.style.height = `${height}px`;
        }
    }

    @action
    setup() {
        this.fetchDefaultProduct.perform();
        this.updatePortalPreview();
    }

    async saveProduct() {
        if (this.product) {
            const stripePrices = this.product.stripePrices || [];
            const monthlyAmount = this.stripeMonthlyAmount * 100;
            const yearlyAmount = this.stripeYearlyAmount * 100;
            const getActivePrice = (prices, type, amount) => {
                return prices.find((price) => {
                    return (
                        price.active && price.amount === amount && price.type === 'recurring' &&
                        price.interval === type && price.currency.toLowerCase() === this.currency.toLowerCase()
                    );
                });
            };
            const monthlyPrice = getActivePrice(stripePrices, 'month', monthlyAmount);
            const yearlyPrice = getActivePrice(stripePrices, 'year', yearlyAmount);

            if (!monthlyPrice) {
                stripePrices.push(
                    {
                        nickname: 'Monthly',
                        amount: monthlyAmount,
                        active: 1,
                        currency: this.currency,
                        interval: 'month',
                        type: 'recurring'
                    }
                );
            }
            if (!yearlyPrice) {
                stripePrices.push(
                    {
                        nickname: 'Yearly',
                        amount: this.stripeYearlyAmount * 100,
                        active: 1,
                        currency: this.currency,
                        interval: 'year',
                        type: 'recurring'
                    }
                );
            }
            if (monthlyPrice && yearlyPrice) {
                this.updatePortalPlans(monthlyPrice.id, yearlyPrice.id);
                this.settings.set('membersMonthlyPriceId', monthlyPrice.id);
                this.settings.set('membersYearlyPriceId', yearlyPrice.id);
                return this.product;
            } else {
                this.product.set('stripePrices', stripePrices);
                const savedProduct = await this.product.save();
                const updatedStripePrices = savedProduct.stripePrices || [];
                const updatedMonthlyPrice = getActivePrice(updatedStripePrices, 'month', monthlyAmount);
                const updatedYearlyPrice = getActivePrice(updatedStripePrices, 'year', yearlyAmount);
                this.updatePortalPlans(updatedMonthlyPrice.id, updatedYearlyPrice.id);
                this.settings.set('membersMonthlyPriceId', updatedMonthlyPrice.id);
                this.settings.set('membersYearlyPriceId', updatedYearlyPrice.id);
                return savedProduct;
            }
        }
    }

    updatePortalPlans(monthlyPriceId, yearlyPriceId) {
        let portalPlans = this.settings.get('portalPlans') || [];
        const currentMontlyPriceId = this.settings.get('membersMonthlyPriceId');
        const currentYearlyPriceId = this.settings.get('membersYearlyPriceId');
        if (portalPlans.includes(currentMontlyPriceId)) {
            portalPlans = portalPlans.filter(priceId => priceId !== currentMontlyPriceId);
            portalPlans.push(monthlyPriceId);
        }

        if (portalPlans.includes(currentYearlyPriceId)) {
            portalPlans = portalPlans.filter(priceId => priceId !== currentYearlyPriceId);
            portalPlans.push(yearlyPriceId);
        }
        this.settings.set('portalPlans', portalPlans);
    }

    getPrice(prices, type) {
        const monthlyPriceId = this.settings.get('membersMonthlyPriceId');
        const yearlyPriceId = this.settings.get('membersYearlyPriceId');

        if (type === 'monthly') {
            return (
                prices.find(price => price.id === monthlyPriceId) ||
                prices.find(price => price.nickname === 'Monthly') ||
                prices.find(price => price.interval === 'month')
            );
        }

        if (type === 'yearly') {
            return (
                prices.find(price => price.id === yearlyPriceId) ||
                prices.find(price => price.nickname === 'Yearly') ||
                prices.find(price => price.interval === 'year')
            );
        }
        return null;
    }

    @task({drop: true})
    *fetchDefaultProduct() {
        const products = yield this.store.query('product', {include: 'stripe_prices'});
        this.product = products.firstObject;
        this.stripePrices = [];
        if (this.product) {
            this.stripePrices = this.product.get('stripePrices') || [];
            const activePrices = this.stripePrices.filter(price => !!price.active);
            const monthlyPrice = this.getPrice(activePrices, 'monthly');
            const yearlyPrice = this.getPrice(activePrices, 'yearly');
            if (monthlyPrice && monthlyPrice.amount) {
                this.stripeMonthlyAmount = (monthlyPrice.amount / 100);
                this.currency = monthlyPrice.currency;
            }
            if (yearlyPrice && yearlyPrice.amount) {
                this.stripeYearlyAmount = (yearlyPrice.amount / 100);
            }
            this.updatePortalPreview();
        }
    }

    @task({drop: true})
    *saveSettingsTask() {
        yield this.validateStripePlans();

        if (this.stripePlanError) {
            return;
        }

        if (this.settings.get('errors').length !== 0) {
            return;
        }

        yield this.saveProduct();
        const result = yield this.settings.save();

        this.updatePortalPreview();

        return result;
    }

    reset() {
        this.showLeaveRouteModal = false;
        this.showLeavePortalModal = false;
        this.showPortalSettings = false;
    }
}
<|endoftext|>


<commit_before>'use strict';

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const mkdirpAsync = Promise.promisify(require('mkdirp'));
const path = require('path');
const optimist = require('optimist');
const express = require('express');
const multer = require('multer');
const bodyParser = require('body-parser');
const shortId = require('shortid');
const dateFormat = require('dateformat');
const pkg = require('./package.json');
const Matcher = require('./lib/matcher.js');
const Renderer = require('./lib/renderer.js');
const Helpers = require('./lib/helpers.js');
const Indexer = require('./lib/indexer.js');

const args = optimist
  .usage(`\n${pkg.name} ${pkg.version}\nUsage: $0 [root dir] [options]`)
  .alias('p', 'port')
  .describe('p', 'Port number to listen on')
  .default('p', 4040)
  .alias('h', 'host')
  .describe('h', 'Host address to bind to')
  .default('h', 'localhost')
  .alias('i', 'images-dir')
  .describe('i', 'Directory to store images')
  .default('i', 'images')
  .alias('o', 'open')
  .boolean('o')
  .describe('o', 'Open default browser on start')
  .describe('help', 'Show this help')
  .argv;

if (args.help || args._.length > 1) {
  optimist.showHelp(console.log);
  process.exit();
}

// Find node_modules base path
let modulesBasePath = require.resolve('highlight.js');
modulesBasePath = modulesBasePath.substr(0, modulesBasePath.lastIndexOf('node_modules'));

const docPath = args._[0] || './';
const rootPath = path.resolve(docPath);
const imagesPath = path.join(rootPath, Helpers.sanitizePath(args.i));
const customCssFile = 'custom.css';
const customStylePath = path.join(rootPath, customCssFile);
const hasCustomCss = fs.existsSync(customStylePath);
const indexer = new Indexer(rootPath);
const renderer = new Renderer(indexer);
const app = express();
const lastModifiedDateFormat = 'yyyy-mm-dd h:MM:ss';

app.set('views', path.join(__dirname, '/views'));
app.set('view engine', 'pug');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use('/_hads/', express.static(path.join(__dirname, '/public')));
app.use('/_hads/highlight/', express.static(path.join(modulesBasePath, 'node_modules/highlight.js/styles')));
app.use('/_hads/octicons/', express.static(path.join(modulesBasePath, 'node_modules/octicons/build/font')));
app.use('/_hads/font-awesome/', express.static(path.join(modulesBasePath, 'node_modules/font-awesome')));
app.use('/_hads/ace/', express.static(path.join(modulesBasePath, 'node_modules/ace-builds/src-min/')));
app.use('/_hads/mermaid/', express.static(path.join(modulesBasePath, 'node_modules/mermaid/dist/')));
app.use('/_hads/dropzone/', express.static(path.join(modulesBasePath, 'node_modules/dropzone/dist/min/')));

if (hasCustomCss) {
  app.use(`/_hads/${customCssFile}`, express.static(customStylePath));
}

const ROOT_FILES = [
  'index.md',
  'README.md',
  'readme.md'
];
const SCRIPTS = [
  '/ace/ace.js',
  '/mermaid/mermaid.min.js',
  '/dropzone/dropzone.min.js',
  '/js/client.js'
];
const STYLESHEETS = [
  '/highlight/github.css',
  '/octicons/octicons.css',
  '/css/github.css',
  '/css/style.css',
  '/font-awesome/css/font-awesome.css'
].concat(hasCustomCss ? [`/${customCssFile}`] : []);

app.get('*', (req, res, next) => {
  let route = Helpers.extractRoute(req.path);
  const query = req.query || {};
  let rootIndex = -1;
  let mdIndex = -1;
  const create = Helpers.hasQueryOption(query, 'create');
  let edit = Helpers.hasQueryOption(query, 'edit') || create;
  let statusCode = 200;
  let filePath, icon, search, error, title, lastModified, contentPromise;

  function renderPage() {
    if (error) {
      edit = false;
      contentPromise = Promise.resolve(renderer.renderMarkdown(error));
      icon = 'octicon-alert';
    } else if (search) {
      contentPromise = renderer.renderSearch(query.search);
      icon = 'octicon-search';
    } else if (Helpers.hasQueryOption(query, 'raw') || Matcher.isImage(filePath)) {
      return res.sendFile(filePath);
    } else if (Matcher.isMarkdown(filePath)) {
      contentPromise = edit ? renderer.renderRaw(filePath) : renderer.renderFile(filePath);
      icon = 'octicon-file';
    } else if (Matcher.isSourceCode(filePath)) {
      contentPromise = renderer.renderSourceCode(filePath, path.extname(filePath).replace('.', ''));
      icon = 'octicon-file-code';
    }

    if (!title) {
      title = search ? renderer.searchResults : path.basename(filePath);
    }

    if (!lastModified) {
      fs.stat(filePath, (err, stats) => {
        lastModified = dateFormat(stats.mtime, lastModifiedDateFormat);
      });
    }

    if (contentPromise) {
      return contentPromise.then(content => {
        res.status(statusCode);
        res.render(edit ? 'edit' : 'file', {
          title,
          lastModified,
          route,
          icon,
          search,
          content,
          styles: STYLESHEETS,
          scripts: SCRIPTS,
          pkg
        });
      });
    }
    next();
  }

  function tryProcessFile() {
    contentPromise = null;
    filePath = path.join(rootPath, route);

    return fs.statAsync(filePath)
      .then(stat => {
        search = query.search && query.search.length > 0 ? query.search.trim() : null;

        if (stat.isDirectory() && !search && !error) {
          if (!create) {
            // Try to find a root file
            route = path.join(route, ROOT_FILES[++rootIndex]);
            return tryProcessFile();
          }
          route = '/';
          title = 'Error';
          error = `Cannot create file \`${filePath}\``;
          statusCode = 400;
        }

        return renderPage();
      })
      .catch(() => {
        if (create) {
          const fixedRoute = Helpers.ensureMarkdownExtension(route);
          if (fixedRoute !== route) {
            return res.redirect(fixedRoute + '?create=1');
          }

          return mkdirpAsync(path.dirname(filePath))
            .then(() => fs.writeFileAsync(filePath, ''))
            .then(() => indexer.updateIndexForFile(filePath))
            .then(tryProcessFile)
            .catch(e => {
              console.error(e);
              title = 'Error';
              error = `Cannot create file \`${filePath}\``;
              route = '/';
              statusCode = 400;
              return renderPage();
            });
        } else if (rootIndex !== -1 && rootIndex < ROOT_FILES.length - 1) {
          route = path.join(path.dirname(route), ROOT_FILES[++rootIndex]);
          return tryProcessFile();
        } else if (rootIndex === -1 && path.basename(route) !== '' && (path.extname(route) === '' || mdIndex > -1) &&
            mdIndex < Matcher.MARKDOWN_EXTENSIONS.length - 1) {
          // Maybe it's a github-style link without extension, let's try adding one
          const extension = Matcher.MARKDOWN_EXTENSIONS[++mdIndex];
          route = path.join(path.dirname(route), `${path.basename(route, path.extname(route))}.${extension}`);
          return tryProcessFile();
        }
        if (path.dirname(route) === path.sep && rootIndex === ROOT_FILES.length - 1) {
          error = '## No home page (╥﹏╥)\nDo you want to create an [index.md](/index.md?create=1) or ' +
              '[readme.md](/readme.md?create=1) file perhaps?';
        } else {
          error = '## File not found ¯\\\\\\_(◕\\_\\_◕)_/¯\n> *There\'s a glitch in the matrix...*';
        }
        title = '404 Error';
        route = '/';
        statusCode = 404;

        return renderPage();
      });
  }

  tryProcessFile();
});

app.post('*', (req, res, next) => {
  const route = Helpers.extractRoute(req.path);
  const filePath = path.join(rootPath, route);

  fs.statAsync(filePath)
    .then(stat => {
      let fileContent = req.body.content;
      if (stat.isFile() && fileContent) {
        if (process.platform !== 'win32') {
          // Www-form-urlencoded data always use CRLF line endings, so this is a quick fix
          fileContent = fileContent.replace(/\r\n/g, '\n');
        }
        return fs.writeFileAsync(filePath, fileContent);
      }
    })
    .then(() => {
      indexer.updateIndexForFile(filePath);
      return renderer.renderFile(filePath);
    })
    .then(content => {
      let lastModified;
      fs.stat(filePath, (err, stats) => {
        lastModified = dateFormat(stats.mtime, lastModifiedDateFormat);
      });

      res.render('file', {
        title: path.basename(filePath),
        lastModified,
        route,
        icon: 'octicon-file',
        content,
        styles: STYLESHEETS,
        scripts: SCRIPTS,
        pkg
      });
    })
    .catch(() => {
      next();
    });
});

app.post('/_hads/upload', [multer({
  storage: multer.diskStorage({
    destination: (req, file, cb) => {
      cb(null, imagesPath);
    },
    filename: (req, file, cb) => {
      mkdirpAsync(imagesPath).then(() => {
        cb(null, shortId.generate() + path.extname(file.originalname));
      });
    }
  }),
  onFileUploadStart: file => !file.mimetype.match(/^image\//),
  limits: {
    fileSize: 1024 * 1024 * 10   // 10 MB
  }
}).single('file'), (req, res) => {
  res.json(path.sep + path.relative(rootPath, req.file.path));
}]);

indexer.indexFiles().then(() => {
  app.listen(args.port, args.host, () => {
    const serverUrl = `http://${args.host}:${args.port}`;
    console.log(`${pkg.name} ${pkg.version} serving at ${serverUrl} (press CTRL+C to exit)`);

    if (args.open) {
      require('open')(serverUrl);
    }
  });
});

exports = app;
<commit_msg>Added error handling while stating file<commit_after>'use strict';

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const mkdirpAsync = Promise.promisify(require('mkdirp'));
const path = require('path');
const optimist = require('optimist');
const express = require('express');
const multer = require('multer');
const bodyParser = require('body-parser');
const shortId = require('shortid');
const dateFormat = require('dateformat');
const pkg = require('./package.json');
const Matcher = require('./lib/matcher.js');
const Renderer = require('./lib/renderer.js');
const Helpers = require('./lib/helpers.js');
const Indexer = require('./lib/indexer.js');

const args = optimist
  .usage(`\n${pkg.name} ${pkg.version}\nUsage: $0 [root dir] [options]`)
  .alias('p', 'port')
  .describe('p', 'Port number to listen on')
  .default('p', 4040)
  .alias('h', 'host')
  .describe('h', 'Host address to bind to')
  .default('h', 'localhost')
  .alias('i', 'images-dir')
  .describe('i', 'Directory to store images')
  .default('i', 'images')
  .alias('o', 'open')
  .boolean('o')
  .describe('o', 'Open default browser on start')
  .describe('help', 'Show this help')
  .argv;

if (args.help || args._.length > 1) {
  optimist.showHelp(console.log);
  process.exit();
}

// Find node_modules base path
let modulesBasePath = require.resolve('highlight.js');
modulesBasePath = modulesBasePath.substr(0, modulesBasePath.lastIndexOf('node_modules'));

const docPath = args._[0] || './';
const rootPath = path.resolve(docPath);
const imagesPath = path.join(rootPath, Helpers.sanitizePath(args.i));
const customCssFile = 'custom.css';
const customStylePath = path.join(rootPath, customCssFile);
const hasCustomCss = fs.existsSync(customStylePath);
const indexer = new Indexer(rootPath);
const renderer = new Renderer(indexer);
const app = express();
const lastModifiedDateFormat = 'yyyy-mm-dd h:MM:ss';

app.set('views', path.join(__dirname, '/views'));
app.set('view engine', 'pug');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use('/_hads/', express.static(path.join(__dirname, '/public')));
app.use('/_hads/highlight/', express.static(path.join(modulesBasePath, 'node_modules/highlight.js/styles')));
app.use('/_hads/octicons/', express.static(path.join(modulesBasePath, 'node_modules/octicons/build/font')));
app.use('/_hads/font-awesome/', express.static(path.join(modulesBasePath, 'node_modules/font-awesome')));
app.use('/_hads/ace/', express.static(path.join(modulesBasePath, 'node_modules/ace-builds/src-min/')));
app.use('/_hads/mermaid/', express.static(path.join(modulesBasePath, 'node_modules/mermaid/dist/')));
app.use('/_hads/dropzone/', express.static(path.join(modulesBasePath, 'node_modules/dropzone/dist/min/')));

if (hasCustomCss) {
  app.use(`/_hads/${customCssFile}`, express.static(customStylePath));
}

const ROOT_FILES = [
  'index.md',
  'README.md',
  'readme.md'
];
const SCRIPTS = [
  '/ace/ace.js',
  '/mermaid/mermaid.min.js',
  '/dropzone/dropzone.min.js',
  '/js/client.js'
];
const STYLESHEETS = [
  '/highlight/github.css',
  '/octicons/octicons.css',
  '/css/github.css',
  '/css/style.css',
  '/font-awesome/css/font-awesome.css'
].concat(hasCustomCss ? [`/${customCssFile}`] : []);

app.get('*', (req, res, next) => {
  let route = Helpers.extractRoute(req.path);
  const query = req.query || {};
  let rootIndex = -1;
  let mdIndex = -1;
  const create = Helpers.hasQueryOption(query, 'create');
  let edit = Helpers.hasQueryOption(query, 'edit') || create;
  let statusCode = 200;
  let filePath, icon, search, error, title, lastModified, contentPromise;

  function renderPage() {
    if (error) {
      edit = false;
      contentPromise = Promise.resolve(renderer.renderMarkdown(error));
      icon = 'octicon-alert';
    } else if (search) {
      contentPromise = renderer.renderSearch(query.search);
      icon = 'octicon-search';
    } else if (Helpers.hasQueryOption(query, 'raw') || Matcher.isImage(filePath)) {
      return res.sendFile(filePath);
    } else if (Matcher.isMarkdown(filePath)) {
      contentPromise = edit ? renderer.renderRaw(filePath) : renderer.renderFile(filePath);
      icon = 'octicon-file';
    } else if (Matcher.isSourceCode(filePath)) {
      contentPromise = renderer.renderSourceCode(filePath, path.extname(filePath).replace('.', ''));
      icon = 'octicon-file-code';
    }

    if (!title) {
      title = search ? renderer.searchResults : path.basename(filePath);
    }

    if (!lastModified) {
      fs.stat(filePath, (err, stats) => {
        if (err) {
          lastModified = 'unknown (error ' + err.code + ')';
        }
        lastModified = dateFormat(stats.mtime, lastModifiedDateFormat);
      });
    }

    if (contentPromise) {
      return contentPromise.then(content => {
        res.status(statusCode);
        res.render(edit ? 'edit' : 'file', {
          title,
          lastModified,
          route,
          icon,
          search,
          content,
          styles: STYLESHEETS,
          scripts: SCRIPTS,
          pkg
        });
      });
    }
    next();
  }

  function tryProcessFile() {
    contentPromise = null;
    filePath = path.join(rootPath, route);

    return fs.statAsync(filePath)
      .then(stat => {
        search = query.search && query.search.length > 0 ? query.search.trim() : null;

        if (stat.isDirectory() && !search && !error) {
          if (!create) {
            // Try to find a root file
            route = path.join(route, ROOT_FILES[++rootIndex]);
            return tryProcessFile();
          }
          route = '/';
          title = 'Error';
          error = `Cannot create file \`${filePath}\``;
          statusCode = 400;
        }

        return renderPage();
      })
      .catch(() => {
        if (create) {
          const fixedRoute = Helpers.ensureMarkdownExtension(route);
          if (fixedRoute !== route) {
            return res.redirect(fixedRoute + '?create=1');
          }

          return mkdirpAsync(path.dirname(filePath))
            .then(() => fs.writeFileAsync(filePath, ''))
            .then(() => indexer.updateIndexForFile(filePath))
            .then(tryProcessFile)
            .catch(e => {
              console.error(e);
              title = 'Error';
              error = `Cannot create file \`${filePath}\``;
              route = '/';
              statusCode = 400;
              return renderPage();
            });
        } else if (rootIndex !== -1 && rootIndex < ROOT_FILES.length - 1) {
          route = path.join(path.dirname(route), ROOT_FILES[++rootIndex]);
          return tryProcessFile();
        } else if (rootIndex === -1 && path.basename(route) !== '' && (path.extname(route) === '' || mdIndex > -1) &&
            mdIndex < Matcher.MARKDOWN_EXTENSIONS.length - 1) {
          // Maybe it's a github-style link without extension, let's try adding one
          const extension = Matcher.MARKDOWN_EXTENSIONS[++mdIndex];
          route = path.join(path.dirname(route), `${path.basename(route, path.extname(route))}.${extension}`);
          return tryProcessFile();
        }
        if (path.dirname(route) === path.sep && rootIndex === ROOT_FILES.length - 1) {
          error = '## No home page (╥﹏╥)\nDo you want to create an [index.md](/index.md?create=1) or ' +
              '[readme.md](/readme.md?create=1) file perhaps?';
        } else {
          error = '## File not found ¯\\\\\\_(◕\\_\\_◕)_/¯\n> *There\'s a glitch in the matrix...*';
        }
        title = '404 Error';
        route = '/';
        statusCode = 404;

        return renderPage();
      });
  }

  tryProcessFile();
});

app.post('*', (req, res, next) => {
  const route = Helpers.extractRoute(req.path);
  const filePath = path.join(rootPath, route);

  fs.statAsync(filePath)
    .then(stat => {
      let fileContent = req.body.content;
      if (stat.isFile() && fileContent) {
        if (process.platform !== 'win32') {
          // Www-form-urlencoded data always use CRLF line endings, so this is a quick fix
          fileContent = fileContent.replace(/\r\n/g, '\n');
        }
        return fs.writeFileAsync(filePath, fileContent);
      }
    })
    .then(() => {
      indexer.updateIndexForFile(filePath);
      return renderer.renderFile(filePath);
    })
    .then(content => {
      let lastModified;
      fs.stat(filePath, (err, stats) => {
        if (err) {
          lastModified = 'unknown (error ' + err.code + ')';
        }
        lastModified = dateFormat(stats.mtime, lastModifiedDateFormat);
      });

      res.render('file', {
        title: path.basename(filePath),
        lastModified,
        route,
        icon: 'octicon-file',
        content,
        styles: STYLESHEETS,
        scripts: SCRIPTS,
        pkg
      });
    })
    .catch(() => {
      next();
    });
});

app.post('/_hads/upload', [multer({
  storage: multer.diskStorage({
    destination: (req, file, cb) => {
      cb(null, imagesPath);
    },
    filename: (req, file, cb) => {
      mkdirpAsync(imagesPath).then(() => {
        cb(null, shortId.generate() + path.extname(file.originalname));
      });
    }
  }),
  onFileUploadStart: file => !file.mimetype.match(/^image\//),
  limits: {
    fileSize: 1024 * 1024 * 10   // 10 MB
  }
}).single('file'), (req, res) => {
  res.json(path.sep + path.relative(rootPath, req.file.path));
}]);

indexer.indexFiles().then(() => {
  app.listen(args.port, args.host, () => {
    const serverUrl = `http://${args.host}:${args.port}`;
    console.log(`${pkg.name} ${pkg.version} serving at ${serverUrl} (press CTRL+C to exit)`);

    if (args.open) {
      require('open')(serverUrl);
    }
  });
});

exports = app;
<|endoftext|>


<commit_before>/**
 * Copyright 2014 - 2015 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: Alex Jacobs
 */

// view is linked to collection when instantiated in goldstone_discover.html

var ZoomablePartitionView = TopologyTreeView.extend({

    defaults: {},

    initSvg: function() {
        var self = this;
        var ns = this.defaults;

        ns.h = 600;
        ns.x = d3.scale.linear().range([0, ns.w]);
        ns.y = d3.scale.linear().range([0, ns.h]);

        ns.vis = d3.select(self.el).append("div")
            .attr("class", "chart")
            .style("width", ns.w + "px")
            .style("height", ns.h + "px")
            .append("svg:svg")
            .attr("width", ns.w)
            .attr("height", ns.h);

        ns.partition = d3.layout.partition()
            .value(function(d) {
                // set to constant for even sizing of nodes
                // this was originally set to d.size
                return 1;
            });
    },

    update: function() {
        var ns = this.defaults;
        var self = this;

        // keep in case data is fed by collection:
        // var root = this.collection.toJSON()[0];
        var root = ns.data;

        var g = ns.vis.selectAll("g")
            .data(ns.partition.nodes(root))
            .enter().append("svg:g")
            .attr("transform", function(d) {
                return "translate(" + ns.x(d.y) + "," + ns.y(d.x) + ")";
            })
            .on("click", click);

        var kx = ns.w / root.dx,
            ky = ns.h / 1;

        g.append("svg:rect")
            .attr("width", root.dy * kx)
            .attr("height", function(d) {
                return d.dx * ky;
            })
            .attr("class", function(d) {
                return d.children ? "parent " + (d.rsrcType || "cloud") + "-icon" : "child" + (d.rsrcType || "cloud") + "-icon";
            })
            .attr("fill", function(d) {
                // return d.children ? "#eee" : "#ddd";
                return "#eee";
            })
            .attr("cursor", function(d) {
                return d.children ? "pointer" : "default";
            })
            .attr({
                "stroke": '#777'
            })
            .attr({
                "fill-opacity": 0.8
            });

        g.append("svg:text")
            .attr("transform", transform)
            .attr("dy", ".35em")
            .style("opacity", function(d) {
                return d.dx * ky > 12 ? 1 : 0;
            })
            .text(function(d) {
                return d.label;
            })
            .attr({
                'font-size': '12px'
            })
            .attr({
                'pointer-events': 'none'
            });

        function imgFile(icon) {
            return "/static/images/" + icon + ".svg";
        }

        var iconMap = {
            icon_backup: ['backups-leaf', 'snapshots-leaf'],
            icon_cloud: ['cloud', 'region'],
            icon_endpoint: ['endpoints-leaf'],
            icon_host: ['host', 'hosts-leaf', 'hypervisors-leaf',
                'servers-leaf'
            ],
            icon_image: ['images-leaf'],
            icon_module: ['module', 'secgroups-leaf'],
            icon_role: ['roles-leaf'],
            icon_service: ['service', 'services-leaf'],
            icon_tenant: ['tenants-leaf'],
            icon_types: ['volume-types-leaf'],
            icon_user: ['users-leaf'],
            icon_volume: ['volume', 'volumes-leaf'],
            icon_vol_transfer: ['agents-leaf', 'transfers-leaf'],
            icon_zone: ['zone', 'aggregates-leaf', 'cloudpipes-leaf',
                'flavors-leaf', 'floating-ip-pools-leaf', 'networks-leaf'
            ]
        };

        g.append("svg:image")
            .attr('x', 2)
            .attr('y', function(d) {
                return (d.dx * ky / 2) - 10;
            })
            .attr('width', 20)
            .attr('height', 20)
            .style("opacity", function(d) {
                return d.dx * ky > 12 ? 1 : 0;
            })
            .attr('xlink:href', function(d) {
                var finalIcon;
                _.each(iconMap, function(classes, icon) {
                    if (classes.indexOf(d.rsrcType) !== -1) {
                        finalIcon = icon;
                    }
                });
                return imgFile(finalIcon);
            });

        d3.select(self.el)
            .on("click", function() {
                click(root);
            });

        function click(d) {
            if (!d.children) {

                // for appending to resource chart header
                var origClickedLabel = d.label;

                if (d.rsrcType.match(/-leaf$/) && ns.leafDataUrls !== undefined) {
                    var url = ns.leafDataUrls[d.rsrcType];
                    if (url !== undefined) {
                        var hasParam = false;
                        if (d.hasOwnProperty('region')) {
                            url = hasParam ? url + "&" : url + "?";
                            hasParam = true;
                            url = url + "region=" + d.region;
                        }
                        if (d.hasOwnProperty('zone')) {
                            url = hasParam ? url + "&" : url + "?";
                            hasParam = true;
                            url = url + "zone=" + d.zone;
                        }

                        // prepend zone to url:
                        var parentModule;
                        // traverse up the tree until the
                        // parent module is reached
                        while (d.rsrcType !== 'module') {
                            d = d.parent;
                        }
                        parentModule = d.label;

                        if (self.overrideSets[d.label]) {
                            ns.filterMultiRsrcDataOverride = self.overrideSets[d.label];
                        } else {
                            ns.filterMultiRsrcDataOverride = null;
                        }

                        url = "/" + parentModule + url;

                        self.loadLeafData(url);
                        self.appendLeafNameToResourceHeader(origClickedLabel);
                    }

                }

                d3.event.stopPropagation();
                return;
            }

            kx = (d.y ? ns.w - 40 : ns.w) / (1 - d.y);
            ky = ns.h / d.dx;
            ns.x.domain([d.y, 1]).range([d.y ? 40 : 0, ns.w]);
            ns.y.domain([d.x, d.x + d.dx]);

            var t = g.transition()
                .duration(d3.event.altKey ? 2500 : 750)
                .attr("transform", function(d) {
                    return "translate(" + ns.x(d.y) + "," + ns.y(d.x) + ")";
                });

            t.select("rect")
                .attr("width", d.dy * kx)
                .attr("height", function(d) {
                    return d.dx * ky;
                });

            t.select("text")
                .attr("transform", transform)
                .style("opacity", function(d) {
                    return d.dx * ky > 12 ? 1 : 0;
                });

            t.select("image")
                .style("opacity", function(d) {
                    return d.dx * ky > 12 ? 1 : 0;
                })
                .attr('x', 2)
                .attr('y', function(d) {
                    return (d.dx * ky / 2) - 10;
                });

            d3.event.stopPropagation();
        }

        function transform(d) {
            return "translate(22," + d.dx * ky / 2 + ")";
        }

    },

    overrideSets: {
        nova: ['@timestamp',
            'metadata',
            'region',
            'links',
            'swap',
            'rxtx_factor',
            'OS-FLV-EXT-DATA:ephemeral',
            'service',
            'cpu_info',
            'hypervisor_version',
            'bridge',
            'bridge_interface',
            'broadcast',
            'cidr_v6',
            'deleted',
            'deleted_at',
            'dhcp_start',
            'dns1',
            'dns2',
            'gateway_v6',
            'host',
            'injected',
            'multi_host',
            'netmask_v6',
            'priority',
            'region',
            'rxtx_base',
            'vpn_private_address',
            'vpn_public_address',
            'vpn_public_port',
            'accessIPv4',
            'accessIPv6',
            'addresses',
            'config_drive',
            'flavor',
            'hostId',
            'image',
            'key_name',
            'links',
            'metadata',
            'OS-DCF:diskConfig',
            'OS-EXT-AZ:availability_zone',
            'OS-EXT-SRV-ATTR:hypervisor_hostname',
            'OS-EXT-STS:power_state',
            'OS-EXT-STS:task_state',
            'OS-EXT-STS:vm_state',
            'os-extended-volumes:volumes_attached',
            'OS-SRV-USG:launched_at',
            'OS-SRV-USG:terminated_at',
            'progress',
            'region',
            'security_groups',
            'rules'
        ],
        cinder: ['@timestamp',
            'metadata',
            'region',
            'extra_specs',
            'display_description',
            'os-extended-snapshot-attributes:progress',
            'links',
            'attachments',
            'availability_zone',
            'os-vol-mig-status-attr:migstat',
            'os-vol-mig-status-attr:name_id',
            'snapshot_id',
            'source_volid'
        ],
        keystone: ['@timestamp'],
        glance: ['@timestamp',
            'metadata',
            'region',
            'tags',
            'checksum',
            'owner',
            'schema',
            'file'
        ]
    },

    template: null

});
<commit_msg>Adds spacing between icon and leaf name.<commit_after>/**
 * Copyright 2014 - 2015 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: Alex Jacobs
 */

// view is linked to collection when instantiated in goldstone_discover.html

var ZoomablePartitionView = TopologyTreeView.extend({

    defaults: {},

    initSvg: function() {
        var self = this;
        var ns = this.defaults;

        // ns.h = 600;
        ns.x = d3.scale.linear().range([0, ns.w]);
        ns.y = d3.scale.linear().range([0, ns.h]);

        ns.vis = d3.select(self.el).append("div")
            .attr("class", "chart")
            .style("width", ns.w + "px")
            .style("height", ns.h + "px")
            .append("svg:svg")
            .attr("width", ns.w)
            .attr("height", ns.h);

        ns.partition = d3.layout.partition()
            .value(function(d) {
                // set to constant for even sizing of nodes
                // this was originally set to d.size
                return 1;
            });
    },

    update: function() {
        var ns = this.defaults;
        var self = this;

        // keep in case data is fed by collection:
        // var root = this.collection.toJSON()[0];
        var root = ns.data;

        var g = ns.vis.selectAll("g")
            .data(ns.partition.nodes(root))
            .enter().append("svg:g")
            .attr("transform", function(d) {
                return "translate(" + ns.x(d.y) + "," + ns.y(d.x) + ")";
            })
            .on("click", click);

        var kx = ns.w / root.dx,
            ky = ns.h / 1;

        g.append("svg:rect")
            .attr("width", root.dy * kx)
            .attr("height", function(d) {
                return d.dx * ky;
            })
            .attr("class", function(d) {
                return d.children ? "parent " + (d.rsrcType || "cloud") + "-icon" : "child" + (d.rsrcType || "cloud") + "-icon";
            })
            .attr("fill", function(d) {
                // return d.children ? "#eee" : "#ddd";
                return "#eee";
            })
            .attr("cursor", function(d) {
                return d.children ? "pointer" : "default";
            })
            .attr({
                "stroke": '#777'
            })
            .attr({
                "fill-opacity": 0.8
            });

        g.append("svg:text")
            .attr("transform", transform)
            .attr("x", 5)
            .attr("dy", ".35em")
            .style("opacity", function(d) {
                return d.dx * ky > 12 ? 1 : 0;
            })
            .text(function(d) {
                return d.label;
            })
            .attr({
                'font-size': '12px'
            })
            .attr({
                'pointer-events': 'none'
            });

        function imgFile(icon) {
            return "/static/images/" + icon + ".svg";
        }

        var iconMap = {
            icon_backup: ['backups-leaf', 'snapshots-leaf'],
            icon_cloud: ['cloud', 'region'],
            icon_endpoint: ['endpoints-leaf'],
            icon_host: ['host', 'hosts-leaf', 'hypervisors-leaf',
                'servers-leaf'
            ],
            icon_image: ['images-leaf'],
            icon_module: ['module', 'secgroups-leaf'],
            icon_role: ['roles-leaf'],
            icon_service: ['service', 'services-leaf'],
            icon_tenant: ['tenants-leaf'],
            icon_types: ['volume-types-leaf'],
            icon_user: ['users-leaf'],
            icon_volume: ['volume', 'volumes-leaf'],
            icon_vol_transfer: ['agents-leaf', 'transfers-leaf'],
            icon_zone: ['zone', 'aggregates-leaf', 'cloudpipes-leaf',
                'flavors-leaf', 'floating-ip-pools-leaf', 'networks-leaf'
            ]
        };

        g.append("svg:image")
            .attr('x', 2)
            .attr('y', function(d) {
                return (d.dx * ky / 2) - 10;
            })
            .attr('width', 20)
            .attr('height', 20)
            .style("opacity", function(d) {
                return d.dx * ky > 12 ? 1 : 0;
            })
            .attr('xlink:href', function(d) {
                var finalIcon;
                _.each(iconMap, function(classes, icon) {
                    if (classes.indexOf(d.rsrcType) !== -1) {
                        finalIcon = icon;
                    }
                });
                return imgFile(finalIcon);
            });

        d3.select(self.el)
            .on("click", function() {
                click(root);
            });

        function click(d) {
            if (!d.children) {

                // for appending to resource chart header
                var origClickedLabel = d.label;

                if (d.rsrcType.match(/-leaf$/) && ns.leafDataUrls !== undefined) {
                    var url = ns.leafDataUrls[d.rsrcType];
                    if (url !== undefined) {
                        var hasParam = false;
                        if (d.hasOwnProperty('region')) {
                            url = hasParam ? url + "&" : url + "?";
                            hasParam = true;
                            url = url + "region=" + d.region;
                        }
                        if (d.hasOwnProperty('zone')) {
                            url = hasParam ? url + "&" : url + "?";
                            hasParam = true;
                            url = url + "zone=" + d.zone;
                        }

                        // prepend zone to url:
                        var parentModule;
                        // traverse up the tree until the
                        // parent module is reached
                        while (d.rsrcType !== 'module') {
                            d = d.parent;
                        }
                        parentModule = d.label;

                        if (self.overrideSets[d.label]) {
                            ns.filterMultiRsrcDataOverride = self.overrideSets[d.label];
                        } else {
                            ns.filterMultiRsrcDataOverride = null;
                        }

                        url = "/" + parentModule + url;

                        self.loadLeafData(url);
                        self.appendLeafNameToResourceHeader(origClickedLabel);
                    }

                }

                d3.event.stopPropagation();
                return;
            }

            kx = (d.y ? ns.w - 40 : ns.w) / (1 - d.y);
            ky = ns.h / d.dx;
            ns.x.domain([d.y, 1]).range([d.y ? 40 : 0, ns.w]);
            ns.y.domain([d.x, d.x + d.dx]);

            var t = g.transition()
                .duration(d3.event.altKey ? 2500 : 750)
                .attr("transform", function(d) {
                    return "translate(" + ns.x(d.y) + "," + ns.y(d.x) + ")";
                });

            t.select("rect")
                .attr("width", d.dy * kx)
                .attr("height", function(d) {
                    return d.dx * ky;
                });

            t.select("text")
                .attr("transform", transform)
                .style("opacity", function(d) {
                    return d.dx * ky > 12 ? 1 : 0;
                });

            t.select("image")
                .style("opacity", function(d) {
                    return d.dx * ky > 12 ? 1 : 0;
                })
                .attr('x', 2)
                .attr('y', function(d) {
                    return (d.dx * ky / 2) - 10;
                });

            d3.event.stopPropagation();
        }

        function transform(d) {
            return "translate(22," + d.dx * ky / 2 + ")";
        }

    },

    overrideSets: {
        nova: ['@timestamp',
            'metadata',
            'region',
            'links',
            'swap',
            'rxtx_factor',
            'OS-FLV-EXT-DATA:ephemeral',
            'service',
            'cpu_info',
            'hypervisor_version',
            'bridge',
            'bridge_interface',
            'broadcast',
            'cidr_v6',
            'deleted',
            'deleted_at',
            'dhcp_start',
            'dns1',
            'dns2',
            'gateway_v6',
            'host',
            'injected',
            'multi_host',
            'netmask_v6',
            'priority',
            'region',
            'rxtx_base',
            'vpn_private_address',
            'vpn_public_address',
            'vpn_public_port',
            'accessIPv4',
            'accessIPv6',
            'addresses',
            'config_drive',
            'flavor',
            'hostId',
            'image',
            'key_name',
            'links',
            'metadata',
            'OS-DCF:diskConfig',
            'OS-EXT-AZ:availability_zone',
            'OS-EXT-SRV-ATTR:hypervisor_hostname',
            'OS-EXT-STS:power_state',
            'OS-EXT-STS:task_state',
            'OS-EXT-STS:vm_state',
            'os-extended-volumes:volumes_attached',
            'OS-SRV-USG:launched_at',
            'OS-SRV-USG:terminated_at',
            'progress',
            'region',
            'security_groups',
            'rules'
        ],
        cinder: ['@timestamp',
            'metadata',
            'region',
            'extra_specs',
            'display_description',
            'os-extended-snapshot-attributes:progress',
            'links',
            'attachments',
            'availability_zone',
            'os-vol-mig-status-attr:migstat',
            'os-vol-mig-status-attr:name_id',
            'snapshot_id',
            'source_volid'
        ],
        keystone: ['@timestamp'],
        glance: ['@timestamp',
            'metadata',
            'region',
            'tags',
            'checksum',
            'owner',
            'schema',
            'file'
        ]
    },

    template: null

});
<|endoftext|>


<commit_before>var alternator = 0;
var cooldown = 0;

function toggleMPD(){
	mpdRequest = new XMLHttpRequest();
	mpdRequest.open("GET", "http://shack.shack:5000/mpd/lounge/toggle", true);
	mpdRequest.setRequestHeader("Content-type","application/json");
	mpdRequest.send();

	if(document.getElementById("MPDbutton").src.split("/").slice(-1)[0] == "logo_mpd_stop.png"){ //I just wanted to select the last element :(
		document.getElementById("MPDbutton").src = "./images_home/logo_mpd_start.png";
	}
	else{
		document.getElementById("MPDbutton").src = "./images_home/logo_mpd_stop.png";
	}

	updateMPDButton();
}

function updateMPDButton(){
	var mpdStatus = new XMLHttpRequest();
	mpdStatus.open("GET", "http://shack.shack:5000/mpd/lounge/status", true);
	mpdStatus.setRequestHeader("Content-type","application/json");

	mpdStatus.onreadystatechange=function(){
		if(mpdStatus.readyState==4 && mpdStatus.status==200){
			response = JSON.parse(mpdStatus.responseText);
			console.log(response);
			if(response.status == "play"){
				document.getElementById("MPDbutton").src = "./images_home/logo_mpd_stop.png";
			}
			else{
				document.getElementById("MPDbutton").src = "./images_home/logo_mpd_start.png";
			}
		}
	}

	mpdStatus.send();

}

function requestMPDInformation(){
	var mpdRequest = null;
	mpdRequest = new XMLHttpRequest();
	mpdRequest.open("GET", "http://shack.shack:5000/mpd/lounge/song", true);
	mpdRequest.setRequestHeader("Content-type","application/json");

	mpdRequest.onreadystatechange=function(){
		if(mpdRequest.readyState==4 && mpdRequest.status==200){
			response = JSON.parse(mpdRequest.responseText);
			console.log(response);
			document.getElementById("mpd").style.backgroundColor = "#440";
			if(!response.file){
				document.getElementById("mpd").innerHTML= "MPD disabled"
			}
			else{
				if(!response.title){
					document.getElementById("mpd").innerHTML= response.file;
				}
				else{
					document.getElementById("mpd").innerHTML= response.title;
				}
			}

			//A little "responsive" scaling here. Not very responsive indeed.
			if(document.getElementById("mpd").innerHTML.length*25 > document.getElementById("mpdDiv").clientWidth){
				document.getElementById("mpd").style.fontSize = "20px";
			}
			else{
				document.getElementById("mpd").style.fontSize = "40px";
			}
		}
	}

	mpdRequest.send();
}

function requestPowerInformation(){
	var powerRequest = null;
	powerRequest = new XMLHttpRequest();
	powerRequest.open("GET", "http://glados.shack/siid/apps/powermeter.py?n=1", true);
	powerRequest.setRequestHeader("Content-type","application/json");

	powerRequest.onreadystatechange=function(){
		if(powerRequest.readyState==4 && powerRequest.status==200){
			response = JSON.parse(powerRequest.responseText);
			console.log(response);
			document.getElementById("power").innerHTML=response.Total[0] + " Watt";
		}
	}

	powerRequest.send();
}

function requestBTCInformation(){
	var btcRequest = null;
	btcRequest = new XMLHttpRequest();
	btcRequest.open("GET", "https://btc-e.com/api/3/ticker/btc_usd", true);
	btcRequest.setRequestHeader("Content-type","application/json");

	btcRequest.onreadystatechange=function(){
		if(btcRequest.readyState==4 && btcRequest.status==200){
			response = JSON.parse(btcRequest.responseText);
			console.log(response);
			document.getElementById("btc").innerHTML=response.btc_usd.avg.toString().substring(0, 5) + "$";
		}
	}

	btcRequest.send();
}

function requestKeyInformation(){
	var keyRequest = new XMLHttpRequest();
	keyRequest.open("GET", "http://portal.shack:8088/status", true);
	keyRequest.setRequestHeader("Content-type","application/json");

	keyRequest.onreadystatechange=function(){
		if(keyRequest.readyState==4 && keyRequest.status==200){
			response = JSON.parse(keyRequest.responseText);
			console.log(response);
			document.getElementById("keyholder").innerHTML="Key: " + response.keyholder;
			if(response.status == "closed"){
				document.getElementById("keyholder").style.backgroundColor = "#f00";
			}
			else{
				document.getElementById("keyholder").style.backgroundColor = "#002";
			}
		}
	}

	keyRequest.send();
}

function requestGelberSack(){
  requestMuell("gelber_sack","Gelber Sack")
}

function requestPapiermuell(){
  requestMuell("papiermuell","Papiermüll")
}

function requestMuell(muell,fancyname){
	var Request = new XMLHttpRequest();
	Request.open("GET", "http://openhab.shack/muellshack/"+muell, true);
	Request.setRequestHeader("Content-type","application/json");

	Request.onreadystatechange=function(){
		if(Request.readyState==4 && Request.status==200){
			response = JSON.parse(Request.responseText);
			var sackdate = new Date(response[muell]);
			if ( isNaN( sackdate.getTime() ) ) {
				document.getElementById(muell).innerHTML= "kein Termin vorhanden";
			} else {
				var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
				document.getElementById(muell).innerHTML= fancyname+": " + sackdate.toLocaleDateString('de-DE', options);
				if(sackdate.getTime() < Date.now()+60*60*24*1000){ //Color the date if its less than 24h away
					if (response['main_action_done']){
						document.getElementById(muell).style.color = "#000";
						document.getElementById(muell).style.backgroundColor = "#ff0";
					}else{
						document.getElementById(muell).style.backgroundColor = "#f00";
					}
          // also set muell img to visible
					document.getElementById(muell+"-img").style.visibility = "visible";
					document.getElementById(muell+"-text").style.visibility = "visible";
				}
				else{
					document.getElementById(muell).style.backgroundColor = "#020";
				}
			}
		}
	}
	Request.send();
}
function requestRestmuell(){
  requestMuell("restmuell","Restmüll")
}

function requestTemp(){
	var tempRequest = new XMLHttpRequest();
	tempRequest.open("GET", "http://smarthome.shack", true);
	tempRequest.setRequestHeader("Content-type","application/json");

	tempRequest.onreadystatechange=function(){
		if(tempRequest.readyState==4 && tempRequest.status==200){
			rt = tempRequest.responseText
			pos = rt.search("Humitidy : <b>")
			envstring = rt.substring(pos+14, pos+23).trim()
			console.log(envstring)
			document.getElementById("power").innerHTML=envstring
		}
	}
	tempRequest.send();
}

function requestNetwork(){
	var tokenRequest =  new XMLHttpRequest();
	tokenRequest.open("GET", "./tokens.txt", true);
	tokenRequest.onreadystatechange=function(){
		if(tokenRequest.readyState==4 && tokenRequest.status==200){
			console.log(tokenRequest.responseText);
			var token = JSON.parse(tokenRequest.responseText).librenms;
			var networkRequest = new XMLHttpRequest();
			networkRequest.open("GET", "http://librenms.shack/api/v0/devices/1/ports/Gi2%2F1", true);
			networkRequest.setRequestHeader("X-Auth-Token", token);
			networkRequest.onreadystatechange=function(){
				if(networkRequest.readyState==4 && networkRequest.status==200){
					var nd = JSON.parse(networkRequest.responseText);
					var downstream = (nd.port.ifInOctets_rate*8/1024/1024);
					var upstream = (nd.port.ifOutOctets_rate*8/1024/1024);
					document.getElementById("mpd").style.fontSize = "38px"; //Reset the font size
					if(downstream > 35 || upstream > 6){
						document.getElementById("mpd").style.backgroundColor = "#f00"; //Color the background red if high load
					}
					document.getElementById("mpd").innerHTML = "▼ " + downstream.toString().substring(0,4) + " Mbit/s (" + (downstream*2).toString().substring(0, 4) + "%) - ▲ " + upstream.toString().substring(0,4) + " Mbit/s (" + (upstream/0.1).toString().substring(0, 4) + "%)";
				}
			}
			networkRequest.send()
		}
	}
	tokenRequest.send()
}

function requestFeinstaub(){
	feinstaubRequest_sued = new XMLHttpRequest();
	feinstaubRequest_sued.open("GET", "http://api.luftdaten.info/v1/data/?sensor=61&page_size=1", true);
	feinstaubRequest_sued.setRequestHeader("Content-type","application/json");
	feinstaubRequest_sued.onreadystatechange=function(){
		if(foo.readyState==4 && foo.status==200){
			response = JSON.parse(foo.responseText);
			for (var idx in response.results[0].sensordatavalues) {
				var sdvalue = response.results[0].sensordatavalues[idx];
				if(sdvalue.value_type == 'P1') {
					console.log(sdvalue.value);
					feinstaub_html += " P1:" + sdvalue.value;
				}
				if(sdvalue.value_type == 'P2') {
					console.log(sdvalue.value);
					feinstaub_html += " P2:" + sdvalue.value;
			  	}
		  	}
		}
		document.getElementById("mpd").innerHTML = feinstaub_html;
	};
	feinstaubRequest_sued.send();
}

function alarm(){
	if(cooldown < 3){
		cooldown++;
	}
	else{
		cooldown = -100; //Prevent double playing
		new Audio('./images_home/alarm.mp3').play();
	}
}

document.onreadystatechange = function() {
	var state = document.readyState;
	if(state == 'complete') {
		requestMPDInformation();
		requestPowerInformation();
		requestBTCInformation();
		requestKeyInformation();
		requestGelberSack();
		requestPapiermuell();
		requestRestmuell();
		updateMPDButton();

		setInterval(function(){
			alternator++; //Increment to allow for changing displays
			if(alternator%3 == 0){ //Alternate the displayed values
				requestPowerInformation();
				requestMPDInformation();
			}
			else if(alternator%3 == 1){
				requestTemp();
				requestNetwork();
			}
			else{
				requestFeinstaub()
				requestPowerInformation()
			}

			requestBTCInformation();
			requestKeyInformation();
			requestGelberSack();
			requestPapiermuell();
			requestRestmuell();
			updateMPDButton();
			cooldown = 0;
		}, 10000);
	}
}
<commit_msg>Migrated Feinstaub to Main Page<commit_after>var alternator = 1;
var cooldown = 0;

function toggleMPD(){
	mpdRequest = new XMLHttpRequest();
	mpdRequest.open("GET", "http://shack.shack:5000/mpd/lounge/toggle", true);
	mpdRequest.setRequestHeader("Content-type","application/json");
	mpdRequest.send();

	if(document.getElementById("MPDbutton").src.split("/").slice(-1)[0] == "logo_mpd_stop.png"){ //I just wanted to select the last element :(
		document.getElementById("MPDbutton").src = "./images_home/logo_mpd_start.png";
	}
	else{
		document.getElementById("MPDbutton").src = "./images_home/logo_mpd_stop.png";
	}

	updateMPDButton();
}

function updateMPDButton(){
	var mpdStatus = new XMLHttpRequest();
	mpdStatus.open("GET", "http://shack.shack:5000/mpd/lounge/status", true);
	mpdStatus.setRequestHeader("Content-type","application/json");

	mpdStatus.onreadystatechange=function(){
		if(mpdStatus.readyState==4 && mpdStatus.status==200){
			response = JSON.parse(mpdStatus.responseText);
			console.log(response);
			if(response.status == "play"){
				document.getElementById("MPDbutton").src = "./images_home/logo_mpd_stop.png";
			}
			else{
				document.getElementById("MPDbutton").src = "./images_home/logo_mpd_start.png";
			}
		}
	}

	mpdStatus.send();

}

function requestMPDInformation(){
	var mpdRequest = null;
	mpdRequest = new XMLHttpRequest();
	mpdRequest.open("GET", "http://shack.shack:5000/mpd/lounge/song", true);
	mpdRequest.setRequestHeader("Content-type","application/json");

	mpdRequest.onreadystatechange=function(){
		if(mpdRequest.readyState==4 && mpdRequest.status==200){
			response = JSON.parse(mpdRequest.responseText);
			console.log(response);
			document.getElementById("mpd").style.backgroundColor = "#440";
			if(!response.file){
				document.getElementById("mpd").innerHTML= "MPD disabled"
			}
			else{
				if(!response.title){
					document.getElementById("mpd").innerHTML= response.file;
				}
				else{
					document.getElementById("mpd").innerHTML= response.title;
				}
			}

			//A little "responsive" scaling here. Not very responsive indeed.
			if(document.getElementById("mpd").innerHTML.length*25 > document.getElementById("mpdDiv").clientWidth){
				document.getElementById("mpd").style.fontSize = "20px";
			}
			else{
				document.getElementById("mpd").style.fontSize = "40px";
			}
		}
	}

	mpdRequest.send();
}

function requestPowerInformation(){
	var powerRequest = null;
	powerRequest = new XMLHttpRequest();
	powerRequest.open("GET", "http://glados.shack/siid/apps/powermeter.py?n=1", true);
	powerRequest.setRequestHeader("Content-type","application/json");

	powerRequest.onreadystatechange=function(){
		if(powerRequest.readyState==4 && powerRequest.status==200){
			response = JSON.parse(powerRequest.responseText);
			console.log(response);
			document.getElementById("power").innerHTML=response.Total[0] + " Watt";
		}
	}

	powerRequest.send();
}

function requestBTCInformation(){
	var btcRequest = null;
	btcRequest = new XMLHttpRequest();
	btcRequest.open("GET", "https://btc-e.com/api/3/ticker/btc_usd", true);
	btcRequest.setRequestHeader("Content-type","application/json");

	btcRequest.onreadystatechange=function(){
		if(btcRequest.readyState==4 && btcRequest.status==200){
			response = JSON.parse(btcRequest.responseText);
			console.log(response);
			document.getElementById("btc").innerHTML=response.btc_usd.avg.toString().substring(0, 5) + "$";
		}
	}

	btcRequest.send();
}

function requestKeyInformation(){
	var keyRequest = new XMLHttpRequest();
	keyRequest.open("GET", "http://portal.shack:8088/status", true);
	keyRequest.setRequestHeader("Content-type","application/json");

	keyRequest.onreadystatechange=function(){
		if(keyRequest.readyState==4 && keyRequest.status==200){
			response = JSON.parse(keyRequest.responseText);
			console.log(response);
			document.getElementById("keyholder").innerHTML="Key: " + response.keyholder;
			if(response.status == "closed"){
				document.getElementById("keyholder").style.backgroundColor = "#f00";
			}
			else{
				document.getElementById("keyholder").style.backgroundColor = "#002";
			}
		}
	}

	keyRequest.send();
}

function requestGelberSack(){
  requestMuell("gelber_sack","Gelber Sack")
}

function requestPapiermuell(){
  requestMuell("papiermuell","Papiermüll")
}

function requestMuell(muell,fancyname){
	var Request = new XMLHttpRequest();
	Request.open("GET", "http://openhab.shack/muellshack/"+muell, true);
	Request.setRequestHeader("Content-type","application/json");

	Request.onreadystatechange=function(){
		if(Request.readyState==4 && Request.status==200){
			response = JSON.parse(Request.responseText);
			var sackdate = new Date(response[muell]);
			if ( isNaN( sackdate.getTime() ) ) {
				document.getElementById(muell).innerHTML= "kein Termin vorhanden";
			} else {
				var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
				document.getElementById(muell).innerHTML= fancyname+": " + sackdate.toLocaleDateString('de-DE', options);
				if(sackdate.getTime() < Date.now()+60*60*24*1000){ //Color the date if its less than 24h away
					if (response['main_action_done']){
						document.getElementById(muell).style.color = "#000";
						document.getElementById(muell).style.backgroundColor = "#ff0";
					}else{
						document.getElementById(muell).style.backgroundColor = "#f00";
					}
          // also set muell img to visible
					document.getElementById(muell+"-img").style.visibility = "visible";
					document.getElementById(muell+"-text").style.visibility = "visible";
				}
				else{
					document.getElementById(muell).style.backgroundColor = "#020";
				}
			}
		}
	}
	Request.send();
}
function requestRestmuell(){
  requestMuell("restmuell","Restmüll")
}

function requestTemp(){
	var tempRequest = new XMLHttpRequest();
	tempRequest.open("GET", "http://smarthome.shack", true);
	tempRequest.setRequestHeader("Content-type","application/json");

	tempRequest.onreadystatechange=function(){
		if(tempRequest.readyState==4 && tempRequest.status==200){
			rt = tempRequest.responseText
			pos = rt.search("Humitidy : <b>")
			envstring = rt.substring(pos+14, pos+23).trim()
			console.log(envstring)
			document.getElementById("power").innerHTML=envstring
		}
	}
	tempRequest.send();
}

function requestNetwork(){
	var tokenRequest =  new XMLHttpRequest();
	tokenRequest.open("GET", "./tokens.txt", true);
	tokenRequest.onreadystatechange=function(){
		if(tokenRequest.readyState==4 && tokenRequest.status==200){
			console.log(tokenRequest.responseText);
			var token = JSON.parse(tokenRequest.responseText).librenms;
			var networkRequest = new XMLHttpRequest();
			networkRequest.open("GET", "http://librenms.shack/api/v0/devices/1/ports/Gi2%2F1", true);
			networkRequest.setRequestHeader("X-Auth-Token", token);
			networkRequest.onreadystatechange=function(){
				if(networkRequest.readyState==4 && networkRequest.status==200){
					var nd = JSON.parse(networkRequest.responseText);
					var downstream = (nd.port.ifInOctets_rate*8/1024/1024);
					var upstream = (nd.port.ifOutOctets_rate*8/1024/1024);
					document.getElementById("mpd").style.fontSize = "38px"; //Reset the font size
					if(downstream > 35 || upstream > 6){
						document.getElementById("mpd").style.backgroundColor = "#f00"; //Color the background red if high load
					}
					document.getElementById("mpd").innerHTML = "▼ " + downstream.toString().substring(0,4) + " Mbit/s (" + (downstream*2).toString().substring(0, 4) + "%) - ▲ " + upstream.toString().substring(0,4) + " Mbit/s (" + (upstream/0.1).toString().substring(0, 4) + "%)";
				}
			}
			networkRequest.send()
		}
	}
	tokenRequest.send()
}

function requestFeinstaub(){
	feinstaubRequest_sued = new XMLHttpRequest();
	feinstaubRequest_sued.open("GET", "http://api.luftdaten.info/v1/data/?sensor=61&page_size=1", true);
	feinstaubRequest_sued.setRequestHeader("Content-type","application/json");
	feinstaubRequest_sued.onreadystatechange=function(){
		feinstaub_html = "Feinstaub: ";
		if(feinstaubRequest_sued.readyState==4 && feinstaubRequest_sued.status==200){
			response = JSON.parse(feinstaubRequest_sued.responseText);
			for (var idx in response.results[0].sensordatavalues) {
				var sdvalue = response.results[0].sensordatavalues[idx];
				if(sdvalue.value_type == 'P1') {
					console.log(sdvalue.value);
					feinstaub_html += " P1:" + sdvalue.value;
				}
				if(sdvalue.value_type == 'P2') {
					console.log(sdvalue.value);
					feinstaub_html += " P2:" + sdvalue.value;
			  	}
		  	}
		}
		document.getElementById("mpd").innerHTML = feinstaub_html;
	};
	feinstaubRequest_sued.send();
}

function alarm(){
	if(cooldown < 3){
		cooldown++;
	}
	else{
		cooldown = -100; //Prevent double playing
		new Audio('./images_home/alarm.mp3').play();
	}
}

document.onreadystatechange = function() {
	var state = document.readyState;
	if(state == 'complete') {
		requestMPDInformation();
		requestPowerInformation();
		requestBTCInformation();
		requestKeyInformation();
		requestGelberSack();
		requestPapiermuell();
		requestRestmuell();
		updateMPDButton();

		setInterval(function(){
			alternator++; //Increment to allow for changing displays
			if(alternator%3 == 0){ //Alternate the displayed values
				requestPowerInformation();
				requestMPDInformation();
			}
			else if(alternator%3 == 1){
				requestTemp();
				requestNetwork();
			}
			else{
				requestFeinstaub()
				requestPowerInformation()
			}

			requestBTCInformation();
			requestKeyInformation();
			requestGelberSack();
			requestPapiermuell();
			requestRestmuell();
			updateMPDButton();
			cooldown = 0;
		}, 10000);
	}
}
<|endoftext|>


<commit_before>$(document).ready(function() {

// Simple nav bar top Hide/Show
  $(".nav_container").on( "click", function() {
    $(this).find(".sneaky").slideToggle().toggleClass(".show");
      if ($(this).find(".sneaky").hasClass(".show")){
        $(this).find(".sneaky").hasClass(".show").slideToggle("slow");
        $(this).find(".sneaky").removeClass(".show").css("display", "none");
      }else{
        $(this).find(".sneaky").addClass(".show").css("display","block");
    };
  });

// Blog Container Hide/Show
  $(".blog_container").on("click", function(){
      $(this).find(".blog_main").slideToggle().toggleClass("active");
      if ($(this).find(".blog_main").hasClass("active")){
        $(this).find(".blog_main").hasClass("active").slideToggle("slow");
        $(this).find(".blog_main").removeClass("active").css("display", "none");
      }else{
        $(this).find(".blog_main").addClass("active");
        $(this).find(".blog_main").hasClass("active").css("display", "block");
    };
  });

//      <----------- Bottom of JS DOC READY ----------->
});

<commit_msg>Logic slide fix<commit_after>$(document).ready(function() {

// Simple nav bar top Hide/Show
  $(".nav_container").on( "click", function() {
    $(this).find(".sneaky").slideToggle().toggleClass(".show");
      if ($(this).find(".sneaky").hasClass(".show")){
        $(this).find(".sneaky").hasClass(".show").slideToggle("slow");
        $(this).find(".sneaky").removeClass(".show").css("display", "none");
      }else{
        $(this).find(".sneaky").addClass(".show").css("display","block");
    };
  });

// Blog Container Hide/Show

  $(".blog_header").on("click", function(){
      $(".blog_container").find(".blog_main").slideToggle().toggleClass("active");
      if ($(".blog_container").find(".blog_main").hasClass("active")){
        $(".blog_container").find(".blog_main").hasClass("active").slideToggle("slow");
        $(".blog_container").find(".blog_main").removeClass("active").css("display", "none");
      }else{
        $(".blog_container").find(".blog_main").addClass("active");
        $(".blog_container").find(".blog_main").hasClass("active").css("display", "block");
    };
  });

//      <----------- Bottom of JS DOC READY ----------->
});

<|endoftext|>