<commit_before>import React from "react";
import styled from "styled-components";
import { Col } from "../Section";
import Link from "gatsby-link";
import Image from "gatsby-image";

const Description = styled.div`
    max-width: 250px;
    margin-top: 56px;
    box-sizing: border-box;
    h2 {
        font-weight: 100;
        line-height: 1.3;
    }
    p:last-child {
        margin-bottom: 0;
    }
`;
const Tag = styled.div`
    padding: 3px 9px;
    line-height: 1.3em;
    border-bottom-right-radius: 4px;
    border-top-right-radius: 4px;
    height: 28px;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    box-sizing: border-box;
    font-size: 12px;
    font-weight: 500;
    text-align: left;
    background-color: ${props =>
        props.theme == "dark"
            ? `rgba(255, 255, 255, 0.2)`
            : `rgba(0, 29, 96, 0.1)`};
`;

const InnerItem = styled.div`
    padding: 26px 26px 56px;
    position: relative;
    z-index: 1;
    min-height: 512px;
    display: flex;
    justify-content: space-between;
    flex-direction: column;
`;

const lightGradient = `linear-gradient(
    to right,
    rgba(255, 255, 255, 0.3),
    rgba(255, 255, 255, 0.3) 140px,
    rgba(255, 255, 255, 0) 290px
)`;

const darkGradient = `linear-gradient(
    to right,
    rgba(0, 0, 0, 0.3),
    rgba(0, 0, 0, 0.3) 140px,
    rgba(0, 0, 0, 0) 290px
)`;

const Overlay = styled.div`
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background-image: ${props =>
        props.theme == "dark" ? darkGradient : lightGradient};
`;
const TagContainer = styled.div`
    display: flex;
    justify-content: space-between;
    @media (min-width: 590px) {
        max-width: 230px;
    }
`;

const WrapWithLink = ({ children, link }) =>
    link && link.indexOf("http") !== 0 ? (
        <Link
            to={link}
            style={{ display: "block", height: "100%", textDecoration: "none" }}
        >
            {children}
        </Link>
    ) : link ? (

            {children}

    ) : (

{children}

    );

const getDevicePixelRatio = function () {
    var ratio = 1;
    if(typeof window === 'undefined') return ratio
    // To account for zoom, change to use deviceXDPI instead of systemXDPI
    if (window.screen.systemXDPI !== undefined && window.screen.logicalXDPI       !== undefined && window.screen.systemXDPI > window.screen.logicalXDPI) {
        // Only allow for values > 1
        ratio = window.screen.systemXDPI / window.screen.logicalXDPI;
    }
    else if (window.devicePixelRatio !== undefined) {
        ratio = window.devicePixelRatio;
    }
    return ratio;
};
const getBackgroundImage = image => {
    return image ? `url("${image.file.url}?h=${ 512 * getDevicePixelRatio() || 1 }&q=50")` : false;
};

const ItemBase = ({
    link,
    cols,
    children,
    tag,
    secondary,
    className,
    theme,
    images
}) => (
    <Col cols={cols} style={{ padding: "4px" }}>
        <WrapWithLink link={link}>

                <Overlay theme={theme} />
                <InnerItem>

                        <TagContainer>
                            {tag ? <Tag theme={theme}>{tag}</Tag> : null}
                            {secondary ? (

{secondary}

                            ) : null}
                        </TagContainer>
                        <Description>{children}</Description>

                        {images.image ? (
                            <Image
                                sizes={images.image.sizes}
                                style={{
                                    margin: "0 auto",
                                    maxWidth: "200px",
                                    width: "100%"
                                }}
                                // resolutions={images.image.resolutions}
                            />
                        ) : null}

                </InnerItem>

        </WrapWithLink>
    </Col>
);

export const Item = styled(ItemBase)`
    background-position: center center;
    background-size: cover;
    height: 100%;
    min-height: 512px;
    text-decoration: none;
    color: ${props => (props.theme == "dark" ? "#FFF" : "#001d60")};
    box-sizing: border-box;
    background-color: ${props => (props.theme == "dark" ? "#333" : "#EBEBEB")};
    position: relative;
    transition: box-shadow 0.15s cubic-bezier(0.4, 0, 0.2, 1);
    a {
        color: inherit;
    }
    &:hover {
        ${props =>
            props.link
                ? `box-shadow: 0 37.125px 70px -12.125px rgba(0, 0, 0, 0.3);
        z-index: 1;
    `
                : null};
    }
    @media (max-width: 580px) {
        min-height: 0;
    }
`;
<commit_msg>Update index.js<commit_after>import React from "react";
import styled from "styled-components";
import { Col } from "../Section";
import Link from "gatsby-link";
import Image from "gatsby-image";

const Description = styled.div`
    max-width: 250px;
    margin-top: 56px;
    box-sizing: border-box;
    h2 {
        font-weight: 500;
        line-height: 1.3;
    }
    p:last-child {
        margin-bottom: 0;
    }
`;
const Tag = styled.div`
    padding: 3px 9px;
    line-height: 1.3em;
    border-bottom-right-radius: 4px;
    border-top-right-radius: 4px;
    height: 28px;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    box-sizing: border-box;
    font-size: 12px;
    font-weight: 500;
    text-align: left;
    background-color: ${props =>
        props.theme == "dark"
            ? `rgba(255, 255, 255, 0.2)`
            : `rgba(0, 29, 96, 0.1)`};
`;

const InnerItem = styled.div`
    padding: 26px 26px 56px;
    position: relative;
    z-index: 1;
    min-height: 512px;
    display: flex;
    justify-content: space-between;
    flex-direction: column;
`;

const lightGradient = `linear-gradient(
    to right,
    rgba(255, 255, 255, 0.3),
    rgba(255, 255, 255, 0.3) 140px,
    rgba(255, 255, 255, 0) 290px
)`;

const darkGradient = `linear-gradient(
    to right,
    rgba(0, 0, 0, 0.3),
    rgba(0, 0, 0, 0.3) 140px,
    rgba(0, 0, 0, 0) 290px
)`;

const Overlay = styled.div`
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background-image: ${props =>
        props.theme == "dark" ? darkGradient : lightGradient};
`;
const TagContainer = styled.div`
    display: flex;
    justify-content: space-between;
    @media (min-width: 590px) {
        max-width: 230px;
    }
`;

const WrapWithLink = ({ children, link }) =>
    link && link.indexOf("http") !== 0 ? (
        <Link
            to={link}
            style={{ display: "block", height: "100%", textDecoration: "none" }}
        >
            {children}
        </Link>
    ) : link ? (

            {children}

    ) : (

{children}

    );

const getDevicePixelRatio = function () {
    var ratio = 1;
    if(typeof window === 'undefined') return ratio
    // To account for zoom, change to use deviceXDPI instead of systemXDPI
    if (window.screen.systemXDPI !== undefined && window.screen.logicalXDPI       !== undefined && window.screen.systemXDPI > window.screen.logicalXDPI) {
        // Only allow for values > 1
        ratio = window.screen.systemXDPI / window.screen.logicalXDPI;
    }
    else if (window.devicePixelRatio !== undefined) {
        ratio = window.devicePixelRatio;
    }
    return ratio;
};
const getBackgroundImage = image => {
    return image ? `url("${image.file.url}?h=${ 512 * getDevicePixelRatio() || 1 }&q=50")` : false;
};

const ItemBase = ({
    link,
    cols,
    children,
    tag,
    secondary,
    className,
    theme,
    images
}) => (
    <Col cols={cols} style={{ padding: "4px" }}>
        <WrapWithLink link={link}>

                <Overlay theme={theme} />
                <InnerItem>

                        <TagContainer>
                            {tag ? <Tag theme={theme}>{tag}</Tag> : null}
                            {secondary ? (

{secondary}

                            ) : null}
                        </TagContainer>
                        <Description>{children}</Description>

                        {images.image ? (
                            <Image
                                sizes={images.image.sizes}
                                style={{
                                    margin: "0 auto",
                                    maxWidth: "200px",
                                    width: "100%"
                                }}
                                // resolutions={images.image.resolutions}
                            />
                        ) : null}

                </InnerItem>

        </WrapWithLink>
    </Col>
);

export const Item = styled(ItemBase)`
    background-position: center center;
    background-size: cover;
    height: 100%;
    min-height: 512px;
    text-decoration: none;
    color: ${props => (props.theme == "dark" ? "#FFF" : "#001d60")};
    box-sizing: border-box;
    background-color: ${props => (props.theme == "dark" ? "#333" : "#EBEBEB")};
    position: relative;
    transition: box-shadow 0.15s cubic-bezier(0.4, 0, 0.2, 1);
    a {
        color: inherit;
    }
    &:hover {
        ${props =>
            props.link
                ? `box-shadow: 0 37.125px 70px -12.125px rgba(0, 0, 0, 0.3);
        z-index: 1;
    `
                : null};
    }
    @media (max-width: 580px) {
        min-height: 0;
    }
`;
<|endoftext|>


<commit_before>'use strict';

const test = require('ava');
const chai = require('chai');
const nock = require('nock');

const integrationHelper = require('../../../helpers/integration');
const stubs = require('../../../helpers/stubs');

chai.should();

test.before(async () => {
  await integrationHelper.hooks.db.connect();
});

test.beforeEach((t) => {
  integrationHelper.hooks.app(t.context);
});

test.afterEach(async () => {
  await integrationHelper.hooks.db.messages.removeAll();
  await integrationHelper.hooks.db.conversations.removeAll();
  nock.cleanAll();
});

test.after.always(async () => {
  await integrationHelper.hooks.db.disconnect();
});

/**
 * GET /
 */
test('GET /api/v2/messages should return 401 if not using valid credentials', async (t) => {
  const res = await t.context.request.get(integrationHelper.routes.v2.messages());
  res.status.should.be.equal(401);
});

test('GET /api/v2/messages should return 404', async (t) => {
  const res = await t.context.request.get(integrationHelper.routes.v2.messages())
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`);
  res.status.should.be.equal(404);
});

test('POST /api/v2/messages?origin=broadcastLite should return 422 if userId is not found', async (t) => {
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload();
  cioWebhookPayload.userId = null;
  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);
  res.status.should.be.equal(422);
});

test('POST /api/v2/messages?origin=broadcastLite should return 422 if broadcastId is not found', async (t) => {
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload();
  cioWebhookPayload.broadcastId = null;
  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);
  res.status.should.be.equal(422);
});

test('POST /api/v2/messages?origin=broadcastLite should return 422 if mobile is not found', async (t) => {
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload();
  cioWebhookPayload.mobile = null;
  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);
  res.status.should.be.equal(422);
});

test('POST /api/v2/messages?origin=broadcastLite should return 422 if mobile is not valid', async (t) => {
  const validMobileNumber = false;
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload(validMobileNumber);

  nock(integrationHelper.routes.gambitCampaigns.baseURI)
    .get(`/broadcasts/${stubs.getBroadcastId()}`)
    .reply(200, stubs.gambitCampaigns.getBroadcastSingleResponse());

  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);

  res.status.should.be.equal(422);
});

test('POST /api/v2/messages?origin=broadcastLite should return 200 if broadcast is sent successfully', async (t) => {
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload();

  nock(integrationHelper.routes.gambitCampaigns.baseURI)
    .get(`/broadcasts/${stubs.getBroadcastId()}`)
    .reply(200, stubs.gambitCampaigns.getBroadcastSingleResponse());

  /**
   * We are using Twilio Test credentials in Wercker.
   * When this runs on wercker we are indeed making a call to the Twilio API.
   * But we do it with the Test credentials so its free and we are not actually
   * sending the text.
   */
  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);

  res.status.should.be.equal(200);
  res.body.data.messages.length.should.be.equal(1);
});
<commit_msg>test for field error message when not sending required params in broadcastLite per review https://github.com/DoSomething/gambit-conversations/pull/407#discussion_r220371259<commit_after>'use strict';

const test = require('ava');
const chai = require('chai');
const nock = require('nock');

const integrationHelper = require('../../../helpers/integration');
const stubs = require('../../../helpers/stubs');

chai.should();

test.before(async () => {
  await integrationHelper.hooks.db.connect();
});

test.beforeEach((t) => {
  integrationHelper.hooks.app(t.context);
});

test.afterEach(async () => {
  await integrationHelper.hooks.db.messages.removeAll();
  await integrationHelper.hooks.db.conversations.removeAll();
  nock.cleanAll();
});

test.after.always(async () => {
  await integrationHelper.hooks.db.disconnect();
});

/**
 * GET /
 */
test('GET /api/v2/messages should return 401 if not using valid credentials', async (t) => {
  const res = await t.context.request.get(integrationHelper.routes.v2.messages());
  res.status.should.be.equal(401);
});

test('GET /api/v2/messages should return 404', async (t) => {
  const res = await t.context.request.get(integrationHelper.routes.v2.messages())
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`);
  res.status.should.be.equal(404);
});

test('POST /api/v2/messages?origin=broadcastLite should return 422 if userId is not found', async (t) => {
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload();
  cioWebhookPayload.userId = null;
  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);

  res.status.should.be.equal(422);
  /**
   * TODO: checking against a hard coded string is brittle.
   * Will break w/ updating the error sent to user. Let's update when we cross that bridge.
   */
  res.body.message.should.include('Missing required userId');
});

test('POST /api/v2/messages?origin=broadcastLite should return 422 if broadcastId is not found', async (t) => {
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload();
  cioWebhookPayload.broadcastId = null;
  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);
  res.status.should.be.equal(422);
  res.body.message.should.include('Missing required broadcastId');
});

test('POST /api/v2/messages?origin=broadcastLite should return 422 if mobile is not found', async (t) => {
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload();
  cioWebhookPayload.mobile = null;
  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);
  res.status.should.be.equal(422);
  res.body.message.should.include('Missing required mobile');
});

test('POST /api/v2/messages?origin=broadcastLite should return 422 if mobile is not valid', async (t) => {
  const validMobileNumber = false;
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload(validMobileNumber);

  nock(integrationHelper.routes.gambitCampaigns.baseURI)
    .get(`/broadcasts/${stubs.getBroadcastId()}`)
    .reply(200, stubs.gambitCampaigns.getBroadcastSingleResponse());

  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);
  res.status.should.be.equal(422);
  res.body.message.should.include('Cannot format mobile number');
});

test('POST /api/v2/messages?origin=broadcastLite should return 200 if broadcast is sent successfully', async (t) => {
  const cioWebhookPayload = stubs.broadcast.getCioWebhookPayload();

  nock(integrationHelper.routes.gambitCampaigns.baseURI)
    .get(`/broadcasts/${stubs.getBroadcastId()}`)
    .reply(200, stubs.gambitCampaigns.getBroadcastSingleResponse());

  /**
   * We are using Twilio Test credentials in Wercker.
   * When this runs on wercker we are indeed making a call to the Twilio API.
   * But we do it with the Test credentials so its free and we are not actually
   * sending the text.
   */
  const res = await t.context.request
    .post(integrationHelper.routes.v2.messages(false, {
      origin: 'broadcastLite',
    }))
    .set('Authorization', `Basic ${integrationHelper.getAuthKey()}`)
    .send(cioWebhookPayload);

  res.status.should.be.equal(200);
  res.body.data.messages.length.should.be.equal(1);
});
<|endoftext|>


<commit_before>define('app/views/machine_add', ['app/views/controlled'],
    /**
     *  Machine Add View
     *
     *  @returns Class
     */
    function (ControlledComponent) {
        return App.MachineAddComponent = ControlledComponent.extend({

            layoutName: 'machine_add',
            controllerName: 'machineAddController',

            changeProviderFlag: false,
            dockerNeedScript: false,
            hasAdvancedScript: false,

            hasLibvirt: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'libvirt' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasDocker: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'docker' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasOpenstack: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'openstack' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasAzure: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'azure' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasPacket: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'packet' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasKey: function() {
                var provider = Mist.machineAddController.newMachineProvider,
                invalids = ['docker'];
                return provider ? (provider.provider ? (((invalids.indexOf(provider.provider) != -1 ? false : true) || (provider.provider == 'docker' && this.get('dockerNeedScript'))) ? true : false) : false) : false;
            }.property('hasDocker', 'dockerNeedScript', 'Mist.machineAddController.newMachineProvider'),

            needsKey: function() {
                return this.get('hasKey') && !this.get('hasLibvirt');
            }.property('hasKey', 'hasLibvirt'),

            hasCloudInit: Ember.computed('Mist.machineAddController.newMachineProvider', function() {
                var provider = Mist.machineAddController.newMachineProvider,
                valids = ['openstack', 'azure', 'digitalocean', 'packet', 'gce', 'rackspace', 'rackspace_first_gen', 'vultr', 'libvirt'];
                return provider ? (provider.provider ? ((valids.indexOf(provider.provider) != -1 || provider.provider.indexOf('ec2') > -1) ? true : false) : false) : false;
            }),

            hasScript: Ember.computed('hasKey', 'dockerNeedScript', function() {
                return this.get('hasKey') || this.get('dockerNeedScript');
            }),

            hasLocation: function() {
                var provider = Mist.machineAddController.newMachineProvider,
                invalids = ['docker', 'indonesiancloud', 'vcloud', 'libvirt'];
                return provider ? (provider.provider ? ((invalids.indexOf(provider.provider) != -1 || provider.locations.model.length == 1) ? false : true) : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasNetworks: function() {
                var provider = Mist.machineAddController.newMachineProvider,
                valids = ['openstack', 'hpcloud', 'vcloud', 'libvirt'];
                return provider ? (provider.provider ? ((valids.indexOf(provider.provider) != -1 && provider.networks.model.length) ? true : false) : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasMonitoring: Ember.computed(function() {
                return Mist.email ? true : false;
            }),

            helpOptions: [{
                field: 'disk-path',
                helpText: 'This is a demo text',
                helpHref: 'http://arsenal.com'
            }, {
                field: 'disk-size',
                helpText: 'This is a demo text',
                helpHref: 'http://arsenal.com'
            }, {
                field: 'existing-disk-path',
                helpText: 'This is a demo text',
                helpHref: 'http://arsenal.com'
            }],

            helpText: '',
            helpHref: '',

            /**
             *  Properties
             */

            price: function () {

                var image = Mist.machineAddController.newMachineImage;
                var size = Mist.machineAddController.newMachineSize;
                var provider = Mist.machineAddController.newMachineProvider;
                var location = Mist.machineAddController.newMachineLocation;

                if (!image || !image.id || !size || !size.id || !provider || !provider.id) return 0;

                try { //might fail with TypeError if no size for this image
                    if (provider.provider.indexOf('ec2') > -1) {
                        if (image.name.indexOf('SUSE Linux Enterprise') > -1)
                            return size.price.sles;
                        if (image.name.indexOf('Red Hat') > -1)
                            return size.price.rhel;
                        return size.price.linux;
                    }
                    if (provider.provider.indexOf('rackspace') > -1) {
                        if (image.name.indexOf('Red Hat') > -1)
                            return size.price.rhel;
                        if (image.name.indexOf('Vyatta') > -1)
                            return size.price.vyatta;
                        return size.price.linux;
                    }
                    if (provider.provider.indexOf('gce') > -1) {
                        if (location.name.indexOf('europe-') > -1)
                            return size.price.eu;
                        if (location.name.indexOf('us-') > -1)
                            return size.price.us;
                        if (location.name.indexOf('asia-') > -1)
                            return size.price.as;
                        return size.price.eu;
                    }
                    return size.price;

                } catch (error) {
                    return 0;
                }
            }.property('Mist.machineAddController.newMachineProvider',
                       'Mist.machineAddController.newMachineImage',
                       'Mist.machineAddController.newMachineSize',
                       'Mist.machineAddController.newMachineLocation'),

            /**
             *
             *  Initialization
             *
             */

             load: function () {
                Ember.run.next(function(){
                    $( "#create-machine" ).collapsible({
                        expand: function(event, ui) {
                            Mist.machineAddController.open(null);

                            var id = $(this).attr('id'),
                            overlay = id ? $('#' + id+'-overlay') : false;
                            if (overlay) {
                                overlay.removeClass('ui-screen-hidden').addClass('in');
                            }
                            $(this).children().next().hide();
                            $(this).children().next().slideDown(250);
                        }
                    });
                });

                // Add event listeners
                Mist.scriptsController.on('onChange', this, 'renderFields');
                Mist.keysController.on('onKeyListChange', this, 'renderFields');
                Mist.cloudsController.on('onImagesChange', this, 'renderFields');

             }.on('didInsertElement'),

             unload: function () {
                Ember.run.next(function(){
                    $( "#create-machine" ).collapsible({
                        collapse: function(event, ui) {
                            $(this).children().next().slideUp(250);
                            var id = $(this).attr('id'),
                            overlay = id ? $('#' + id+'-overlay') : false;
                            if (overlay) {
                                overlay.removeClass('in').addClass('ui-screen-hidden');
                            }
                        }
                    });
                });

                // Remove event listeners
                Mist.scriptsController.off('onChange', this, 'renderFields');
                Mist.keysController.off('onKeyListChange', this, 'renderFields');
                Mist.cloudsController.off('onImagesChange', this, 'renderFields');

             }.on('willDestroyElement'),

            /**
             *
             *  Methods
             *
             */

             clear: function () {
                 this.$('select').val('basic').slider('refresh');
                 this.$('.ui-collapsible').removeClass('selected');
                 this.setProperties({
                    changeProviderFlag: false,
                    dockerNeedScript: false,
                    hasAdvancedScript: false
                });
                $('#create-machine-floating-ip .ui-checkbox > .ui-btn')
                    .removeClass('ui-checkbox-off')
                    .addClass('ui-checkbox-on');
             },

             checkImageSelected: function(image) {
                if (image) {
                    this.triggerAction({
                        action:'selectProvider',
                        target: this,
                        actionContext: image.cloud
                    });

                    this.triggerAction({
                        action:'selectImage',
                        target: this,
                        actionContext: image
                    });
                }
             },

             fieldIsReady: function (field) {
                $('#create-machine')
                    .find('.ui-collapsible')
                    .collapsible()
                    .collapsible('collapse');
                $('#create-machine-' + field).addClass('selected');
             },

             renderFields: function () {
                Ember.run.schedule('afterRender', this, function() {

                    // Render collapsibles
                    if ($('.ui-collapsible').collapsible) {
                        $('.ui-collapsible').collapsible().enhanceWithin();
                    }

                    // Render listviews
                    if ($('.ui-listview').listview) {
                        $('.ui-listview').listview().listview('refresh');
                    }

                    // Render checkboxes
                    if ($('.ember-checkbox').checkboxradio) {
                        $('.ember-checkbox').checkboxradio().checkboxradio('refresh');
                    }
                });
             },

            /**
             *
             *  Actions
             *
             */

            actions: {

                clickOverlay: function() {
                    $('#create-machine').collapsible('collapse');
                },

                switchToggled: function () {
                    var value = this.$('#script select').val();
                    Mist.machineAddController.set('newMachineScript', '');
                    Mist.machineAddController.set('newMachineScriptParams', '');
                    Mist.machineAddController.set('hasScript', value == 'advanced');
                    this.set('hasAdvancedScript', value == 'advanced');
                    this.renderFields();
                },

                createLibvirtImage: function () {
                    var that = this;
                    Mist.machineImageCreateController.open(function(newImagePath) {
                        if (newImagePath) {
                            var imageFaker = Ember.Object.create({
                                id: newImagePath,
                                name: newImagePath
                            });

                            Mist.machineAddController.setProperties({
                                'newMachineImage': imageFaker,
                                'newMachineLibvirtImagePath': newImagePath
                            });

                            that.fieldIsReady('image');
                        }
                    });
                },

                selectProvider: function (cloud) {

                    if (this.fieldIsReady) {
                        this.fieldIsReady('provider');
                    }

                    cloud.networks.model.forEach(function (network, index) {
                        network.set('selected', false);
                    });
                    Mist.machineAddController.set('newMachineLocation', {'name' : 'Select Location'})
                                             .set('newMachineImage', {'name' : 'Select Image'})
                                             .set('newMachineSize', {'name' : 'Select Size'})
                                             .set('newMachineProvider', cloud);

                    // Check we are not on single image page
                    if(! Mist.machineAddController.get('selectedImage')) this.set('changeProviderFlag', true);
                },

                selectImage: function (image) {

                    if (this.fieldIsReady) {
                        this.fieldIsReady('image');
                    }

                    Mist.machineAddController.set('newMachineLocation', {'name' : 'Select Location'})
                                             .set('newMachineSize', {'name' : 'Select Size'})
                                             .set('newMachineImage', image);

                    this.set('dockerNeedScript', this.get('hasDocker') && image.get('isMist'));
                },

                selectSize: function (size) {
                    this.fieldIsReady('size');

                    Mist.machineAddController.set('newMachineLocation', {'name' : 'Select Location'})
                                             .set('newMachineSize', size);
                },

                selectLocation: function (location) {
                    this.fieldIsReady('location');

                    Mist.machineAddController.set('newMachineLocation', location);
                },

                selectKey: function (key) {
                    this._selectKey(key)
                },

                selectProject: function(project) {
                    this.fieldIsReady('project');

                    Mist.machineAddController.set('newMachineProject', project);
                },

                selectScript: function (script) {
                    Mist.machineAddController.set('newMachineScript', script);
                    $('#create-machine-script-select').collapsible().collapsible('collapse');
                },

                toggleNetworkSelection: function (network) {
                    network.set('selected', !network.selected);
                },

                createKeyClicked: function () {
                    var that = this;
                    Mist.keyAddController.open(function (success, key) {
                        that._selectKey(key);
                    });
                },

                backClicked: function () {
                    Mist.machineAddController.close();
                },

                launchClicked: function () {
                    Mist.machineAddController.add();
                },

                helpClicked: function(field) {
                    var helper = this.get('helpOptions').findBy('field', field);
                    this.setProperties({
                        helpText: helper.helpText,
                        helpHref: helper.helpHref
                    });

                    Ember.run.schedule('afterRender', this, function () {
                        $('#help-tooltip').popup().popup('option', 'positionTo', '#create-machine-' + field + '-helper');
                        $('#help-tooltip').popup('open');
                    });
                }
            },

            _selectKey: function (key) {
                this.fieldIsReady('key');

                Mist.machineAddController.set('newMachineKey', key);
            },

            /**
             *
             *  Observers
             *
             */

             monitoringObserver: function() {
                Ember.run.once(this, 'renderFields');
             }.observes('Mist.machineAddController.newMachineMonitoring'),

             bindingsObserver: function () {
                Ember.run.once(this, 'renderFields');
             }.observes('Mist.machineAddController.newMachineSize',
                        'Mist.machineAddController.newMachineImage',
                        'Mist.machineAddController.newMachineProvider',
                        'Mist.machineAddController.newMachineLocation'),

             providerObserver: function() {
                 Ember.run.once(this, function(){
                    if (this.changeProviderFlag) Mist.machineAddController._resetProvider();
                 });
             }.observes('Mist.machineAddController.newMachineProvider'),

             networksObserver: function() {
                 Ember.run.once(this, function () {
                    if (this.get('hasOpenstack')) {
                        if (Mist.machineAddController.newMachineProvider.networks.model.filterBy('selected', true).length) {
                            $('#create-machine-floating-ip').slideDown();
                        } else {
                            $('#create-machine-floating-ip').slideUp();
                        }
                    }
                 });
             }.observes('hasOpenstack', 'Mist.machineAddController.newMachineProvider.networks.model.@each.selected')
        });
    }
);
<commit_msg>remove tiny<commit_after>define('app/views/machine_add', ['app/views/controlled'],
    /**
     *  Machine Add View
     *
     *  @returns Class
     */
    function (ControlledComponent) {
        return App.MachineAddComponent = ControlledComponent.extend({

            layoutName: 'machine_add',
            controllerName: 'machineAddController',

            changeProviderFlag: false,
            dockerNeedScript: false,
            hasAdvancedScript: false,

            hasLibvirt: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'libvirt' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasDocker: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'docker' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasOpenstack: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'openstack' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasAzure: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'azure' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasPacket: function() {
                var provider = Mist.machineAddController.newMachineProvider;
                return provider ? (provider.provider && provider.provider == 'packet' ? true : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasKey: function() {
                var provider = Mist.machineAddController.newMachineProvider,
                invalids = ['docker'];
                return provider ? (provider.provider ? (((invalids.indexOf(provider.provider) != -1 ? false : true) || (provider.provider == 'docker' && this.get('dockerNeedScript'))) ? true : false) : false) : false;
            }.property('hasDocker', 'dockerNeedScript', 'Mist.machineAddController.newMachineProvider'),

            needsKey: function() {
                return this.get('hasKey') && !this.get('hasLibvirt');
            }.property('hasKey', 'hasLibvirt'),

            hasCloudInit: Ember.computed('Mist.machineAddController.newMachineProvider', function() {
                var provider = Mist.machineAddController.newMachineProvider,
                valids = ['openstack', 'azure', 'digitalocean', 'packet', 'gce', 'rackspace', 'rackspace_first_gen', 'vultr', 'libvirt'];
                return provider ? (provider.provider ? ((valids.indexOf(provider.provider) != -1 || provider.provider.indexOf('ec2') > -1) ? true : false) : false) : false;
            }),

            hasScript: Ember.computed('hasKey', 'dockerNeedScript', function() {
                return this.get('hasKey') || this.get('dockerNeedScript');
            }),

            hasLocation: function() {
                var provider = Mist.machineAddController.newMachineProvider,
                invalids = ['docker', 'indonesiancloud', 'vcloud', 'libvirt'];
                return provider ? (provider.provider ? ((invalids.indexOf(provider.provider) != -1 || provider.locations.model.length == 1) ? false : true) : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasNetworks: function() {
                var provider = Mist.machineAddController.newMachineProvider,
                valids = ['openstack', 'hpcloud', 'vcloud', 'libvirt'];
                return provider ? (provider.provider ? ((valids.indexOf(provider.provider) != -1 && provider.networks.model.length) ? true : false) : false) : false;
            }.property('Mist.machineAddController.newMachineProvider'),

            hasMonitoring: Ember.computed(function() {
                return Mist.email ? true : false;
            }),

            helpOptions: [{
                field: 'disk-path',
                helpText: 'This is a demo text',
                helpHref: 'http://arsenal.com'
            }, {
                field: 'disk-size',
                helpText: 'This is a demo text',
                helpHref: 'http://arsenal.com'
            }],

            helpText: '',
            helpHref: '',

            /**
             *  Properties
             */

            price: function () {

                var image = Mist.machineAddController.newMachineImage;
                var size = Mist.machineAddController.newMachineSize;
                var provider = Mist.machineAddController.newMachineProvider;
                var location = Mist.machineAddController.newMachineLocation;

                if (!image || !image.id || !size || !size.id || !provider || !provider.id) return 0;

                try { //might fail with TypeError if no size for this image
                    if (provider.provider.indexOf('ec2') > -1) {
                        if (image.name.indexOf('SUSE Linux Enterprise') > -1)
                            return size.price.sles;
                        if (image.name.indexOf('Red Hat') > -1)
                            return size.price.rhel;
                        return size.price.linux;
                    }
                    if (provider.provider.indexOf('rackspace') > -1) {
                        if (image.name.indexOf('Red Hat') > -1)
                            return size.price.rhel;
                        if (image.name.indexOf('Vyatta') > -1)
                            return size.price.vyatta;
                        return size.price.linux;
                    }
                    if (provider.provider.indexOf('gce') > -1) {
                        if (location.name.indexOf('europe-') > -1)
                            return size.price.eu;
                        if (location.name.indexOf('us-') > -1)
                            return size.price.us;
                        if (location.name.indexOf('asia-') > -1)
                            return size.price.as;
                        return size.price.eu;
                    }
                    return size.price;

                } catch (error) {
                    return 0;
                }
            }.property('Mist.machineAddController.newMachineProvider',
                       'Mist.machineAddController.newMachineImage',
                       'Mist.machineAddController.newMachineSize',
                       'Mist.machineAddController.newMachineLocation'),

            /**
             *
             *  Initialization
             *
             */

             load: function () {
                Ember.run.next(function(){
                    $( "#create-machine" ).collapsible({
                        expand: function(event, ui) {
                            Mist.machineAddController.open(null);

                            var id = $(this).attr('id'),
                            overlay = id ? $('#' + id+'-overlay') : false;
                            if (overlay) {
                                overlay.removeClass('ui-screen-hidden').addClass('in');
                            }
                            $(this).children().next().hide();
                            $(this).children().next().slideDown(250);
                        }
                    });
                });

                // Add event listeners
                Mist.scriptsController.on('onChange', this, 'renderFields');
                Mist.keysController.on('onKeyListChange', this, 'renderFields');
                Mist.cloudsController.on('onImagesChange', this, 'renderFields');

             }.on('didInsertElement'),

             unload: function () {
                Ember.run.next(function(){
                    $( "#create-machine" ).collapsible({
                        collapse: function(event, ui) {
                            $(this).children().next().slideUp(250);
                            var id = $(this).attr('id'),
                            overlay = id ? $('#' + id+'-overlay') : false;
                            if (overlay) {
                                overlay.removeClass('in').addClass('ui-screen-hidden');
                            }
                        }
                    });
                });

                // Remove event listeners
                Mist.scriptsController.off('onChange', this, 'renderFields');
                Mist.keysController.off('onKeyListChange', this, 'renderFields');
                Mist.cloudsController.off('onImagesChange', this, 'renderFields');

             }.on('willDestroyElement'),

            /**
             *
             *  Methods
             *
             */

             clear: function () {
                 this.$('select').val('basic').slider('refresh');
                 this.$('.ui-collapsible').removeClass('selected');
                 this.setProperties({
                    changeProviderFlag: false,
                    dockerNeedScript: false,
                    hasAdvancedScript: false
                });
                $('#create-machine-floating-ip .ui-checkbox > .ui-btn')
                    .removeClass('ui-checkbox-off')
                    .addClass('ui-checkbox-on');
             },

             checkImageSelected: function(image) {
                if (image) {
                    this.triggerAction({
                        action:'selectProvider',
                        target: this,
                        actionContext: image.cloud
                    });

                    this.triggerAction({
                        action:'selectImage',
                        target: this,
                        actionContext: image
                    });
                }
             },

             fieldIsReady: function (field) {
                $('#create-machine')
                    .find('.ui-collapsible')
                    .collapsible()
                    .collapsible('collapse');
                $('#create-machine-' + field).addClass('selected');
             },

             renderFields: function () {
                Ember.run.schedule('afterRender', this, function() {

                    // Render collapsibles
                    if ($('.ui-collapsible').collapsible) {
                        $('.ui-collapsible').collapsible().enhanceWithin();
                    }

                    // Render listviews
                    if ($('.ui-listview').listview) {
                        $('.ui-listview').listview().listview('refresh');
                    }

                    // Render checkboxes
                    if ($('.ember-checkbox').checkboxradio) {
                        $('.ember-checkbox').checkboxradio().checkboxradio('refresh');
                    }
                });
             },

            /**
             *
             *  Actions
             *
             */

            actions: {

                clickOverlay: function() {
                    $('#create-machine').collapsible('collapse');
                },

                switchToggled: function () {
                    var value = this.$('#script select').val();
                    Mist.machineAddController.set('newMachineScript', '');
                    Mist.machineAddController.set('newMachineScriptParams', '');
                    Mist.machineAddController.set('hasScript', value == 'advanced');
                    this.set('hasAdvancedScript', value == 'advanced');
                    this.renderFields();
                },

                createLibvirtImage: function () {
                    var that = this;
                    Mist.machineImageCreateController.open(function(newImagePath) {
                        if (newImagePath) {
                            var imageFaker = Ember.Object.create({
                                id: newImagePath,
                                name: newImagePath
                            });

                            Mist.machineAddController.setProperties({
                                'newMachineImage': imageFaker,
                                'newMachineLibvirtImagePath': newImagePath
                            });

                            that.fieldIsReady('image');
                        }
                    });
                },

                selectProvider: function (cloud) {

                    if (this.fieldIsReady) {
                        this.fieldIsReady('provider');
                    }

                    cloud.networks.model.forEach(function (network, index) {
                        network.set('selected', false);
                    });
                    Mist.machineAddController.set('newMachineLocation', {'name' : 'Select Location'})
                                             .set('newMachineImage', {'name' : 'Select Image'})
                                             .set('newMachineSize', {'name' : 'Select Size'})
                                             .set('newMachineProvider', cloud);

                    // Check we are not on single image page
                    if(! Mist.machineAddController.get('selectedImage')) this.set('changeProviderFlag', true);
                },

                selectImage: function (image) {

                    if (this.fieldIsReady) {
                        this.fieldIsReady('image');
                    }

                    Mist.machineAddController.set('newMachineLocation', {'name' : 'Select Location'})
                                             .set('newMachineSize', {'name' : 'Select Size'})
                                             .set('newMachineImage', image);

                    this.set('dockerNeedScript', this.get('hasDocker') && image.get('isMist'));
                },

                selectSize: function (size) {
                    this.fieldIsReady('size');

                    Mist.machineAddController.set('newMachineLocation', {'name' : 'Select Location'})
                                             .set('newMachineSize', size);
                },

                selectLocation: function (location) {
                    this.fieldIsReady('location');

                    Mist.machineAddController.set('newMachineLocation', location);
                },

                selectKey: function (key) {
                    this._selectKey(key)
                },

                selectProject: function(project) {
                    this.fieldIsReady('project');

                    Mist.machineAddController.set('newMachineProject', project);
                },

                selectScript: function (script) {
                    Mist.machineAddController.set('newMachineScript', script);
                    $('#create-machine-script-select').collapsible().collapsible('collapse');
                },

                toggleNetworkSelection: function (network) {
                    network.set('selected', !network.selected);
                },

                createKeyClicked: function () {
                    var that = this;
                    Mist.keyAddController.open(function (success, key) {
                        that._selectKey(key);
                    });
                },

                backClicked: function () {
                    Mist.machineAddController.close();
                },

                launchClicked: function () {
                    Mist.machineAddController.add();
                },

                helpClicked: function(field) {
                    var helper = this.get('helpOptions').findBy('field', field);
                    this.setProperties({
                        helpText: helper.helpText,
                        helpHref: helper.helpHref
                    });

                    Ember.run.schedule('afterRender', this, function () {
                        $('#help-tooltip').popup().popup('option', 'positionTo', '#create-machine-' + field + '-helper');
                        $('#help-tooltip').popup('open');
                    });
                }
            },

            _selectKey: function (key) {
                this.fieldIsReady('key');

                Mist.machineAddController.set('newMachineKey', key);
            },

            /**
             *
             *  Observers
             *
             */

             monitoringObserver: function() {
                Ember.run.once(this, 'renderFields');
             }.observes('Mist.machineAddController.newMachineMonitoring'),

             bindingsObserver: function () {
                Ember.run.once(this, 'renderFields');
             }.observes('Mist.machineAddController.newMachineSize',
                        'Mist.machineAddController.newMachineImage',
                        'Mist.machineAddController.newMachineProvider',
                        'Mist.machineAddController.newMachineLocation'),

             providerObserver: function() {
                 Ember.run.once(this, function(){
                    if (this.changeProviderFlag) Mist.machineAddController._resetProvider();
                 });
             }.observes('Mist.machineAddController.newMachineProvider'),

             networksObserver: function() {
                 Ember.run.once(this, function () {
                    if (this.get('hasOpenstack')) {
                        if (Mist.machineAddController.newMachineProvider.networks.model.filterBy('selected', true).length) {
                            $('#create-machine-floating-ip').slideDown();
                        } else {
                            $('#create-machine-floating-ip').slideUp();
                        }
                    }
                 });
             }.observes('hasOpenstack', 'Mist.machineAddController.newMachineProvider.networks.model.@each.selected')
        });
    }
);
<|endoftext|>


<commit_before>/**
 * Manage downloads
 */

(function() {

var cache = {};
var form = $('form');
var minified = true;

var dependencies = {};

var treeURL = 'https://api.github.com/repos/PrismJS/prism/git/trees/gh-pages?recursive=1';
var treePromise = new Promise(function(resolve) {
	$u.xhr({
		url: treeURL,
		callback: function(xhr) {
			if (xhr.status < 400) {
				resolve(JSON.parse(xhr.responseText).tree);
			}
		}
	});
});

var qstr = window.location.search.match(/(?:languages|plugins)=[-+\w]+|themes=[-\w]+/g);
if (qstr) {
	qstr.forEach(function(str) {
		var kv = str.split('=', 2),
		    category = kv[0],
		    ids = kv[1].split('+');
		if (category !== 'meta' && category !== 'core' && components[category]) {
			for (var id in components[category]) {
				if (components[category][id].option) {
					delete components[category][id].option;
				}
			}
			ids.forEach(function(id) {
				if (id !== 'meta') {
					if (components[category][id]) {
						var requireId = id;
						while (requireId && components[category][requireId] && components[category][requireId].option !== 'default') {
							if (typeof components[category][requireId] === 'string') {
								components[category][requireId] = { title: components[category][requireId] }
							}
							components[category][requireId].option = 'default';
							requireId = components[category][requireId].require;
						}
					}
				}
			});
		}
	});
}

for (var category in components) {
	var all = components[category];

	all.meta.section = $u.element.create('section', {
		className: 'options',
		id: 'category-' + category,
		contents: {
			tag: 'h1',
			contents: category.charAt(0).toUpperCase() + category.slice(1)
		},
		inside: '#components'
	});

	if (all.meta.addCheckAll) {
		$u.element.create('label', {
			attributes: {
				'data-id': 'check-all-' + category
			},
			contents: [
				{
					tag: 'input',
					properties: {
						type: 'checkbox',
						name: 'check-all-' + category,
						value: '',
						checked: false,
						onclick: (function(category, all){
							return function () {
								var checkAll = this;
								$$('input[name="download-' + category + '"]').forEach(function(input) {
									all[input.value].enabled = input.checked = checkAll.checked;
								});

								update(category);
							};
						})(category, all)
					}
				},
				'Select/unselect all'
			],
			inside: all.meta.section
		});
	}

	for (var id in all) {
		if(id === 'meta') {
			continue;
		}

		var checked = false, disabled = false;
		var option = all[id].option || all.meta.option;

		switch (option) {
			case 'mandatory': disabled = true; // fallthrough
			case 'default': checked = true;
		}

		var filepath = all.meta.path.replace(/\{id}/g, id);

		var info = all[id] = {
			title: all[id].title || all[id],
			noCSS: all[id].noCSS || all.meta.noCSS,
			noJS: all[id].noJS || all.meta.noJS,
			enabled: checked,
			require: $u.type(all[id].require) === 'string' ? [all[id].require] : all[id].require,
			after: $u.type(all[id].after) === 'string' ? [all[id].after] : all[id].after,
			owner: all[id].owner,
			files: {
				minified: {
					paths: [],
					size: 0
				},
				dev: {
					paths: [],
					size: 0
				}
			}
		};

		if (info.require) {
			info.require.forEach(function (v) {
				dependencies[v] = (dependencies[v] || []).concat(id);
			});
		}

		if (!all[id].noJS && !/\.css$/.test(filepath)) {
			info.files.minified.paths.push(filepath.replace(/(\.js)?$/, '.min.js'));
			info.files.dev.paths.push(filepath.replace(/(\.js)?$/, '.js'));
		}

		if ((!all[id].noCSS && !/\.js$/.test(filepath)) || /\.css$/.test(filepath)) {
			var cssFile = filepath.replace(/(\.css)?$/, '.css');

			info.files.minified.paths.push(cssFile);
			info.files.dev.paths.push(cssFile);
		}

		$u.element.create('label', {
			attributes: {
				'data-id': id
			},
			contents: [
				{
					tag: 'input',
					properties: {
						type: all.meta.exclusive? 'radio' : 'checkbox',
						name: 'download-' + category,
						value: id,
						checked: checked,
						disabled: disabled,
						onclick: (function(id, category, all){
							return function () {
								$$('input[name="' + this.name + '"]').forEach(function(input) {
									all[input.value].enabled = input.checked;
								});

								if (all[id].require && this.checked) {
									all[id].require.forEach(function(v) {
										var input = $('label[data-id="' + v + '"] > input');
										input.checked = true;

										input.onclick();
									});
								}

								if (dependencies[id] && !this.checked) { // It’s required by others
									dependencies[id].forEach(function(dependent) {
										var input = $('label[data-id="' + dependent + '"] > input');
										input.checked = false;

										input.onclick();
									});
								}

								update(category, id);
							};
						})(id, category, all)
					}
				},
				all.meta.link? {
					tag: 'a',
					properties: {
						href: all.meta.link.replace(/\{id}/g, id)
					},
					contents: info.title
				} : info.title,
				all[id].owner? {
					tag: 'a',
					properties: {
						href: 'http://github.com/' + all[id].owner,
						className: 'owner',
						target: '_blank'
					},
					contents: all[id].owner
				} : ' ',
				{
					tag: 'strong',
					className: 'filesize'
				}
			],
			inside: all.meta.section
		});
	}
}

form.elements.compression[0].onclick =
form.elements.compression[1].onclick = function() {
	minified = !!+this.value;

	getFilesSizes();
};

function getFileSize(filepath) {
	return treePromise.then(function(tree) {
		for(var i=0, l=tree.length; i<l; i++) {
			if(tree[i].path === filepath) {
				return tree[i].size;
			}
		}
	});
}

function getFilesSizes() {
	for (var category in components) {
		var all = components[category];

		for (var id in all) {
			if(id === 'meta') {
				continue;
			}

			var distro = all[id].files[minified? 'minified' : 'dev'],
			    files = distro.paths;

			files.forEach(function (filepath) {
				var file = cache[filepath] = cache[filepath] || {};

				if(!file.size) {

					(function(category, id) {
					getFileSize(filepath).then(function(size) {
						if(size) {
							file.size = size;
							distro.size += file.size;

							update(category, id);
						}
					});
					}(category, id));
				}
				else {
					update(category, id);
				}
			});
		}
	}
}

getFilesSizes();

function getFileContents(filepath) {
	return new Promise(function(resolve, reject) {
		$u.xhr({
			url: filepath,
			callback: function(xhr) {
				if (xhr.status < 400 && xhr.responseText) {
					resolve(xhr.responseText);
				} else {
					reject();
				}
			}
		});
	});
}

function prettySize(size) {
	return Math.round(100 * size / 1024)/100 + 'KB';
}

function update(updatedCategory, updatedId){
	// Update total size
	var total = {js: 0, css: 0}, updated = {js: 0, css: 0};

	for (var category in components) {
		var all = components[category];
		var allChecked = true;

		for (var id in all) {
			var info = all[id];

			if (info.enabled || id == updatedId) {
				var distro = info.files[minified? 'minified' : 'dev'];

				distro.paths.forEach(function(path) {
					if (cache[path]) {
						var file = cache[path];

						var type = path.match(/\.(\w+)$/)[1],
						    size = file.size || 0;

						if (info.enabled) {

							if (!file.contentsPromise) {
								file.contentsPromise = getFileContents(path);
							}

							total[type] += size;
						}

						if (id == updatedId) {
							updated[type] += size;
						}
					}
				});
			}
			if (id !== 'meta' && !info.enabled) {
				allChecked = false;
			}
		}

		if (all.meta.addCheckAll) {
			$('input[name="check-all-' + category + '"]').checked = allChecked;
		}
	}

	total.all = total.js + total.css;

	if (updatedId) {
		updated.all = updated.js + updated.css;

		$u.element.prop($('label[data-id="' + updatedId + '"] .filesize'), {
			textContent: prettySize(updated.all),
			title: (updated.js ? Math.round(100 * updated.js / updated.all) + '% JavaScript' : '') +
				(updated.js && updated.css ? ' + ' : '') +
				(updated.css ? Math.round(100 * updated.css / updated.all) + '% CSS' : '')
		});
	}

	$('#filesize').textContent = prettySize(total.all);

	$u.element.prop($('#percent-js'), {
		textContent: Math.round(100 * total.js / total.all) + '%',
		title: prettySize(total.js)
	});

	$u.element.prop($('#percent-css'), {
		textContent: Math.round(100 * total.css / total.all) + '%',
		title: prettySize(total.css)
	});

	delayedGenerateCode();
}

var timerId = 0;
// "debounce" multiple rapid requests to generate and highlight code
function delayedGenerateCode(){
	if ( timerId !== 0 ) {
		clearTimeout(timerId);
	}
	timerId = setTimeout(generateCode, 500);
}

function getSortedComponents(components, requireName, sorted) {
	if (!sorted) {
		sorted = [];
		for (var component in components) {
			sorted.push(component);
		}
	}

	var i = 0;
	while (i < sorted.length) {
		var id = sorted[i];
		var indexOfRequirement = i;
		var notNow = false;
		for (var requirement in components[id][requireName]) {
			indexOfRequirement = sorted.indexOf(components[id][requireName][requirement]);
			if (indexOfRequirement > i) {
				notNow = true;
				break;
			}
		}
		if (notNow) {
			tmp = sorted[i];
			sorted[i] = sorted[indexOfRequirement];
			sorted[indexOfRequirement] = tmp;
		}
		else {
			i++;
		}
	}
	return sorted;
}

function getSortedComponentsByRequirements(components){
	var sorted = getSortedComponents(components, "after");
	return getSortedComponents(components, "require", sorted);
}

function generateCode(){
	var promises = [];
	var redownload = {};

	for (var category in components) {
		var all = components[category];

		// In case if one component requires other, required component should go first.
		var sorted = getSortedComponentsByRequirements(all);

		for (var i = 0; i < sorted.length; i++) {
			var id = sorted[i];

			if(id === 'meta') {
				continue;
			}

			var info = all[id];
			if (info.enabled) {
				if (category !== 'core') {
					redownload[category] = redownload[category]  || [];
					redownload[category].push(id);
				}
				info.files[minified? 'minified' : 'dev'].paths.forEach(function (path) {
					if (cache[path]) {
						var type = path.match(/\.(\w+)$/)[1];

						promises.push({
							contentsPromise: cache[path].contentsPromise,
							path: path,
							type: type
						});
					}
				});
			}
		}
	}

	// Hide error message if visible
	var error = $('#download .error');
	error.style.display = '';

	buildCode(promises).then(function(res) {
		var code = res.code;
		var errors = res.errors;

		if(errors.length) {
			error.style.display = 'block';
			error.innerHTML = '';
			$u.element.contents(error, errors);
		}

		var redownloadUrl = window.location.href.split("?")[0] + "?";
		for (var category in redownload) {
			redownloadUrl += category + "=" + redownload[category].join('+') + "&";
		}
		redownloadUrl = "/* " + redownloadUrl.replace(/&$/,"") + " */";

		for (var type in code) {
			var codeElement = $('#download-' + type + ' code');

			codeElement.textContent = redownloadUrl + "\n" + code[type];
			Prism.highlightElement(codeElement, true);

			$('#download-' + type + ' .download-button').href = 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(redownloadUrl + "\n" + code[type]);
		}
	});
}

function buildCode(promises) {
	var i = 0,
	    l = promises.length;
	var code = {js: '', css: ''};
	var errors = [];

	var f = function(resolve) {
		if(i < l) {
			var p = promises[i];
			p.contentsPromise.then(function(contents) {
				code[p.type] += contents + (p.type === 'js' && !/;\s*$/.test(contents) ? ';' : '') + '\n';
				i++;
				f(resolve);
			});
			p.contentsPromise['catch'](function() {
				errors.push($u.element.create({
					tag: 'p',
					prop: {
						textContent: 'An error occurred while fetching the file "' + p.path + '".'
					}
				}));
				i++;
				f(resolve);
			});
		} else {
			resolve({code: code, errors: errors});
		}
	};

	return new Promise(f);
}

})();<commit_msg>Download page: Fix implicitly declared variable<commit_after>/**
 * Manage downloads
 */

(function() {

var cache = {};
var form = $('form');
var minified = true;

var dependencies = {};

var treeURL = 'https://api.github.com/repos/PrismJS/prism/git/trees/gh-pages?recursive=1';
var treePromise = new Promise(function(resolve) {
	$u.xhr({
		url: treeURL,
		callback: function(xhr) {
			if (xhr.status < 400) {
				resolve(JSON.parse(xhr.responseText).tree);
			}
		}
	});
});

var qstr = window.location.search.match(/(?:languages|plugins)=[-+\w]+|themes=[-\w]+/g);
if (qstr) {
	qstr.forEach(function(str) {
		var kv = str.split('=', 2),
		    category = kv[0],
		    ids = kv[1].split('+');
		if (category !== 'meta' && category !== 'core' && components[category]) {
			for (var id in components[category]) {
				if (components[category][id].option) {
					delete components[category][id].option;
				}
			}
			ids.forEach(function(id) {
				if (id !== 'meta') {
					if (components[category][id]) {
						var requireId = id;
						while (requireId && components[category][requireId] && components[category][requireId].option !== 'default') {
							if (typeof components[category][requireId] === 'string') {
								components[category][requireId] = { title: components[category][requireId] }
							}
							components[category][requireId].option = 'default';
							requireId = components[category][requireId].require;
						}
					}
				}
			});
		}
	});
}

for (var category in components) {
	var all = components[category];

	all.meta.section = $u.element.create('section', {
		className: 'options',
		id: 'category-' + category,
		contents: {
			tag: 'h1',
			contents: category.charAt(0).toUpperCase() + category.slice(1)
		},
		inside: '#components'
	});

	if (all.meta.addCheckAll) {
		$u.element.create('label', {
			attributes: {
				'data-id': 'check-all-' + category
			},
			contents: [
				{
					tag: 'input',
					properties: {
						type: 'checkbox',
						name: 'check-all-' + category,
						value: '',
						checked: false,
						onclick: (function(category, all){
							return function () {
								var checkAll = this;
								$$('input[name="download-' + category + '"]').forEach(function(input) {
									all[input.value].enabled = input.checked = checkAll.checked;
								});

								update(category);
							};
						})(category, all)
					}
				},
				'Select/unselect all'
			],
			inside: all.meta.section
		});
	}

	for (var id in all) {
		if(id === 'meta') {
			continue;
		}

		var checked = false, disabled = false;
		var option = all[id].option || all.meta.option;

		switch (option) {
			case 'mandatory': disabled = true; // fallthrough
			case 'default': checked = true;
		}

		var filepath = all.meta.path.replace(/\{id}/g, id);

		var info = all[id] = {
			title: all[id].title || all[id],
			noCSS: all[id].noCSS || all.meta.noCSS,
			noJS: all[id].noJS || all.meta.noJS,
			enabled: checked,
			require: $u.type(all[id].require) === 'string' ? [all[id].require] : all[id].require,
			after: $u.type(all[id].after) === 'string' ? [all[id].after] : all[id].after,
			owner: all[id].owner,
			files: {
				minified: {
					paths: [],
					size: 0
				},
				dev: {
					paths: [],
					size: 0
				}
			}
		};

		if (info.require) {
			info.require.forEach(function (v) {
				dependencies[v] = (dependencies[v] || []).concat(id);
			});
		}

		if (!all[id].noJS && !/\.css$/.test(filepath)) {
			info.files.minified.paths.push(filepath.replace(/(\.js)?$/, '.min.js'));
			info.files.dev.paths.push(filepath.replace(/(\.js)?$/, '.js'));
		}

		if ((!all[id].noCSS && !/\.js$/.test(filepath)) || /\.css$/.test(filepath)) {
			var cssFile = filepath.replace(/(\.css)?$/, '.css');

			info.files.minified.paths.push(cssFile);
			info.files.dev.paths.push(cssFile);
		}

		$u.element.create('label', {
			attributes: {
				'data-id': id
			},
			contents: [
				{
					tag: 'input',
					properties: {
						type: all.meta.exclusive? 'radio' : 'checkbox',
						name: 'download-' + category,
						value: id,
						checked: checked,
						disabled: disabled,
						onclick: (function(id, category, all){
							return function () {
								$$('input[name="' + this.name + '"]').forEach(function(input) {
									all[input.value].enabled = input.checked;
								});

								if (all[id].require && this.checked) {
									all[id].require.forEach(function(v) {
										var input = $('label[data-id="' + v + '"] > input');
										input.checked = true;

										input.onclick();
									});
								}

								if (dependencies[id] && !this.checked) { // It’s required by others
									dependencies[id].forEach(function(dependent) {
										var input = $('label[data-id="' + dependent + '"] > input');
										input.checked = false;

										input.onclick();
									});
								}

								update(category, id);
							};
						})(id, category, all)
					}
				},
				all.meta.link? {
					tag: 'a',
					properties: {
						href: all.meta.link.replace(/\{id}/g, id)
					},
					contents: info.title
				} : info.title,
				all[id].owner? {
					tag: 'a',
					properties: {
						href: 'http://github.com/' + all[id].owner,
						className: 'owner',
						target: '_blank'
					},
					contents: all[id].owner
				} : ' ',
				{
					tag: 'strong',
					className: 'filesize'
				}
			],
			inside: all.meta.section
		});
	}
}

form.elements.compression[0].onclick =
form.elements.compression[1].onclick = function() {
	minified = !!+this.value;

	getFilesSizes();
};

function getFileSize(filepath) {
	return treePromise.then(function(tree) {
		for(var i=0, l=tree.length; i<l; i++) {
			if(tree[i].path === filepath) {
				return tree[i].size;
			}
		}
	});
}

function getFilesSizes() {
	for (var category in components) {
		var all = components[category];

		for (var id in all) {
			if(id === 'meta') {
				continue;
			}

			var distro = all[id].files[minified? 'minified' : 'dev'],
			    files = distro.paths;

			files.forEach(function (filepath) {
				var file = cache[filepath] = cache[filepath] || {};

				if(!file.size) {

					(function(category, id) {
					getFileSize(filepath).then(function(size) {
						if(size) {
							file.size = size;
							distro.size += file.size;

							update(category, id);
						}
					});
					}(category, id));
				}
				else {
					update(category, id);
				}
			});
		}
	}
}

getFilesSizes();

function getFileContents(filepath) {
	return new Promise(function(resolve, reject) {
		$u.xhr({
			url: filepath,
			callback: function(xhr) {
				if (xhr.status < 400 && xhr.responseText) {
					resolve(xhr.responseText);
				} else {
					reject();
				}
			}
		});
	});
}

function prettySize(size) {
	return Math.round(100 * size / 1024)/100 + 'KB';
}

function update(updatedCategory, updatedId){
	// Update total size
	var total = {js: 0, css: 0}, updated = {js: 0, css: 0};

	for (var category in components) {
		var all = components[category];
		var allChecked = true;

		for (var id in all) {
			var info = all[id];

			if (info.enabled || id == updatedId) {
				var distro = info.files[minified? 'minified' : 'dev'];

				distro.paths.forEach(function(path) {
					if (cache[path]) {
						var file = cache[path];

						var type = path.match(/\.(\w+)$/)[1],
						    size = file.size || 0;

						if (info.enabled) {

							if (!file.contentsPromise) {
								file.contentsPromise = getFileContents(path);
							}

							total[type] += size;
						}

						if (id == updatedId) {
							updated[type] += size;
						}
					}
				});
			}
			if (id !== 'meta' && !info.enabled) {
				allChecked = false;
			}
		}

		if (all.meta.addCheckAll) {
			$('input[name="check-all-' + category + '"]').checked = allChecked;
		}
	}

	total.all = total.js + total.css;

	if (updatedId) {
		updated.all = updated.js + updated.css;

		$u.element.prop($('label[data-id="' + updatedId + '"] .filesize'), {
			textContent: prettySize(updated.all),
			title: (updated.js ? Math.round(100 * updated.js / updated.all) + '% JavaScript' : '') +
				(updated.js && updated.css ? ' + ' : '') +
				(updated.css ? Math.round(100 * updated.css / updated.all) + '% CSS' : '')
		});
	}

	$('#filesize').textContent = prettySize(total.all);

	$u.element.prop($('#percent-js'), {
		textContent: Math.round(100 * total.js / total.all) + '%',
		title: prettySize(total.js)
	});

	$u.element.prop($('#percent-css'), {
		textContent: Math.round(100 * total.css / total.all) + '%',
		title: prettySize(total.css)
	});

	delayedGenerateCode();
}

var timerId = 0;
// "debounce" multiple rapid requests to generate and highlight code
function delayedGenerateCode(){
	if ( timerId !== 0 ) {
		clearTimeout(timerId);
	}
	timerId = setTimeout(generateCode, 500);
}

function getSortedComponents(components, requireName, sorted) {
	if (!sorted) {
		sorted = [];
		for (var component in components) {
			sorted.push(component);
		}
	}

	var i = 0;
	while (i < sorted.length) {
		var id = sorted[i];
		var indexOfRequirement = i;
		var notNow = false;
		for (var requirement in components[id][requireName]) {
			indexOfRequirement = sorted.indexOf(components[id][requireName][requirement]);
			if (indexOfRequirement > i) {
				notNow = true;
				break;
			}
		}
		if (notNow) {
			var tmp = sorted[i];
			sorted[i] = sorted[indexOfRequirement];
			sorted[indexOfRequirement] = tmp;
		}
		else {
			i++;
		}
	}
	return sorted;
}

function getSortedComponentsByRequirements(components){
	var sorted = getSortedComponents(components, "after");
	return getSortedComponents(components, "require", sorted);
}

function generateCode(){
	var promises = [];
	var redownload = {};

	for (var category in components) {
		var all = components[category];

		// In case if one component requires other, required component should go first.
		var sorted = getSortedComponentsByRequirements(all);

		for (var i = 0; i < sorted.length; i++) {
			var id = sorted[i];

			if(id === 'meta') {
				continue;
			}

			var info = all[id];
			if (info.enabled) {
				if (category !== 'core') {
					redownload[category] = redownload[category]  || [];
					redownload[category].push(id);
				}
				info.files[minified? 'minified' : 'dev'].paths.forEach(function (path) {
					if (cache[path]) {
						var type = path.match(/\.(\w+)$/)[1];

						promises.push({
							contentsPromise: cache[path].contentsPromise,
							path: path,
							type: type
						});
					}
				});
			}
		}
	}

	// Hide error message if visible
	var error = $('#download .error');
	error.style.display = '';

	buildCode(promises).then(function(res) {
		var code = res.code;
		var errors = res.errors;

		if(errors.length) {
			error.style.display = 'block';
			error.innerHTML = '';
			$u.element.contents(error, errors);
		}

		var redownloadUrl = window.location.href.split("?")[0] + "?";
		for (var category in redownload) {
			redownloadUrl += category + "=" + redownload[category].join('+') + "&";
		}
		redownloadUrl = "/* " + redownloadUrl.replace(/&$/,"") + " */";

		for (var type in code) {
			var codeElement = $('#download-' + type + ' code');

			codeElement.textContent = redownloadUrl + "\n" + code[type];
			Prism.highlightElement(codeElement, true);

			$('#download-' + type + ' .download-button').href = 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(redownloadUrl + "\n" + code[type]);
		}
	});
}

function buildCode(promises) {
	var i = 0,
	    l = promises.length;
	var code = {js: '', css: ''};
	var errors = [];

	var f = function(resolve) {
		if(i < l) {
			var p = promises[i];
			p.contentsPromise.then(function(contents) {
				code[p.type] += contents + (p.type === 'js' && !/;\s*$/.test(contents) ? ';' : '') + '\n';
				i++;
				f(resolve);
			});
			p.contentsPromise['catch'](function() {
				errors.push($u.element.create({
					tag: 'p',
					prop: {
						textContent: 'An error occurred while fetching the file "' + p.path + '".'
					}
				}));
				i++;
				f(resolve);
			});
		} else {
			resolve({code: code, errors: errors});
		}
	};

	return new Promise(f);
}

})();<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';

import {cloneRepository, buildRepository, setUpLocalAndRemoteRepositories} from '../helpers';

import FileSystemChangeObserver from '../../lib/models/file-system-change-observer';

describe('FileSystemChangeObserver', function() {
  beforeEach(function() {
    this.timeout(5000); // increase the timeout because we're interacting with file system events.
  });

  it('emits an event when a project file is modified, created, or deleted', async function() {
    const workdirPath = await cloneRepository('three-files');
    const repository = await buildRepository(workdirPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'a change\n');
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.calledOnce);

    changeSpy.reset();
    fs.writeFileSync(path.join(workdirPath, 'new-file.txt'), 'a change\n');
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.calledOnce);

    changeSpy.reset();
    fs.unlinkSync(path.join(workdirPath, 'a.txt'));
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.calledOnce);
  });

  it('emits an event when a file is staged or unstaged', async function() {
    const workdirPath = await cloneRepository('three-files');
    const repository = await buildRepository(workdirPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'a change\n');
    await repository.git.exec(['add', 'a.txt']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);

    changeSpy.reset();
    await repository.git.exec(['reset', 'a.txt']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);
  });

  it('emits an event when a branch is checked out', async function() {
    const workdirPath = await cloneRepository('three-files');
    const repository = await buildRepository(workdirPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    await repository.git.exec(['checkout', '-b', 'new-branch']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);
  });

  it('emits an event when commits are pushed', async function() {
    const {localRepoPath} = await setUpLocalAndRemoteRepositories();
    const repository = await buildRepository(localRepoPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    let changePromise = changeObserver.getLastChangePromise();
    await repository.git.exec(['commit', '--allow-empty', '-m', 'new commit']);
    await changePromise;

    changeSpy.reset();
    changePromise = changeObserver.getLastChangePromise();
    await repository.git.exec(['push', 'origin', 'master']);
    await changePromise;
    assert.isTrue(changeSpy.called);
  });

  it('emits an event when a new tracking branch is added after pushing', async function() {
    const {localRepoPath} = await setUpLocalAndRemoteRepositories();
    const repository = await buildRepository(localRepoPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    await repository.git.exec(['checkout', '-b', 'new-branch']);
    await changeObserver.lastFileChangePromise;

    changeSpy.reset();
    await repository.git.exec(['push', '--set-upstream', 'origin', 'new-branch']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);
  });

  it('emits an event when commits have been fetched', async function() {
    const {localRepoPath} = await setUpLocalAndRemoteRepositories({remoteAhead: true});
    const repository = await buildRepository(localRepoPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    await repository.git.exec(['fetch', 'origin', 'master']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);
  });
});
<commit_msg>Use fancy new async assertion instead of lastFileChangePromise<commit_after>import fs from 'fs';
import path from 'path';

import {cloneRepository, buildRepository, setUpLocalAndRemoteRepositories, until} from '../helpers';

import FileSystemChangeObserver from '../../lib/models/file-system-change-observer';

describe('FileSystemChangeObserver', function() {
  beforeEach(function() {
    this.timeout(5000); // increase the timeout because we're interacting with file system events.
  });

  it('emits an event when a project file is modified, created, or deleted', async function() {
    const workdirPath = await cloneRepository('three-files');
    const repository = await buildRepository(workdirPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'a change\n');
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.calledOnce);

    changeSpy.reset();
    fs.writeFileSync(path.join(workdirPath, 'new-file.txt'), 'a change\n');
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.calledOnce);

    changeSpy.reset();
    fs.unlinkSync(path.join(workdirPath, 'a.txt'));
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.calledOnce);
  });

  it('emits an event when a file is staged or unstaged', async function() {
    const workdirPath = await cloneRepository('three-files');
    const repository = await buildRepository(workdirPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'a change\n');
    await repository.git.exec(['add', 'a.txt']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);

    changeSpy.reset();
    await repository.git.exec(['reset', 'a.txt']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);
  });

  it('emits an event when a branch is checked out', async function() {
    const workdirPath = await cloneRepository('three-files');
    const repository = await buildRepository(workdirPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    await repository.git.exec(['checkout', '-b', 'new-branch']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);
  });

  it('emits an event when commits are pushed', async function() {
    const {localRepoPath} = await setUpLocalAndRemoteRepositories();
    const repository = await buildRepository(localRepoPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    await repository.git.exec(['commit', '--allow-empty', '-m', 'new commit']);

    changeSpy.reset();
    await repository.git.exec(['push', 'origin', 'master']);
    assert.async.isTrue(changeSpy.called);
  });

  it('emits an event when a new tracking branch is added after pushing', async function() {
    const {localRepoPath} = await setUpLocalAndRemoteRepositories();
    const repository = await buildRepository(localRepoPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    await repository.git.exec(['checkout', '-b', 'new-branch']);
    await changeObserver.lastFileChangePromise;

    changeSpy.reset();
    await repository.git.exec(['push', '--set-upstream', 'origin', 'new-branch']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);
  });

  it('emits an event when commits have been fetched', async function() {
    const {localRepoPath} = await setUpLocalAndRemoteRepositories({remoteAhead: true});
    const repository = await buildRepository(localRepoPath);
    const changeSpy = sinon.spy();
    const changeObserver = new FileSystemChangeObserver(repository);
    changeObserver.onDidChange(changeSpy);
    await changeObserver.start();

    await repository.git.exec(['fetch', 'origin', 'master']);
    await changeObserver.lastFileChangePromise;
    assert.isTrue(changeSpy.called);
  });
});
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';

import '/imports/ui/components/buttons/buttons.js';

import './course-roles.html';

Template.courseRole.created = function() {
	this.enrolling = new ReactiveVar(false);
	this.busy(false);
};

Template.courseRole.helpers({
	enrolling: function() { return Template.instance().enrolling.get(); },

	roleSubscribe: function() {
		let role = this.type;
		if (role == 'participant') role = 'interested';

		return 'roles.' + role + '.subscribe';
	},

	roleSubscribed: function() {
		let role = this.type;
		if (role == 'participant') role = 'interested';

		return 'roles.' + role + '.subscribed';
	},

	maySubscribe: function(role) {
		var operator = Meteor.userId();

		// Show the participation buttons even when not logged-in.
		// fun HACK: if we pass an arbitrary string instead of falsy
		// the maySubscribe() will return true if the user could subscribe
		// if they were logged-in. Plain abuse of maySubscribe().
		if (!operator) operator = 'unlogged';

		return maySubscribe(operator, this.course, operator, role);
	}
});

Template.courseRole.events({
	'click .js-role-enroll-btn'(event, instance) {
		// if (PleaseLogin()) return;
		event.preventDefault();
		instance.enrolling.set(true);
		// return false;
	},

	'click .js-role-subscribe-btn'(event, instance) {
		event.preventDefault();

		const comment = instance.$('.js-comment').val();
		instance.busy('enrolling');
		instance.autorun((computation) => {
			const userId = Meteor.userId();
			if (userId) {
				computation.stop();
				Meteor.call('add_role', this.course._id, userId, this.roletype.type, err => {
					instance.busy(false);
					if (err) {
						console.error(err);
					} else {
						Meteor.call('change_comment', this.course._id, comment);
						instance.enrolling.set(false);
					}
				});
			} else {
				Session.set('pleaseLogin', true);
				$('#accountTasks').modal('show');
			}
		});
	},

	'click .js-role-enroll-cancel': function (e, template) {
		template.enrolling.set(false);
		return false;
	},

	'click .js-role-unsubscribe-btn': function () {
		Meteor.call('remove_role', this.course._id, Meteor.userId(), this.roletype.type);
		return false;
	}
});
<commit_msg>make sure everything is done when enrolling<commit_after>import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';

import '/imports/ui/components/buttons/buttons.js';

import './course-roles.html';

Template.courseRole.created = function() {
	this.enrolling = new ReactiveVar(false);
	this.busy(false);
};

Template.courseRole.helpers({
	enrolling: function() { return Template.instance().enrolling.get(); },

	roleSubscribe: function() {
		let role = this.type;
		if (role == 'participant') role = 'interested';

		return 'roles.' + role + '.subscribe';
	},

	roleSubscribed: function() {
		let role = this.type;
		if (role == 'participant') role = 'interested';

		return 'roles.' + role + '.subscribed';
	},

	maySubscribe: function(role) {
		var operator = Meteor.userId();

		// Show the participation buttons even when not logged-in.
		// fun HACK: if we pass an arbitrary string instead of falsy
		// the maySubscribe() will return true if the user could subscribe
		// if they were logged-in. Plain abuse of maySubscribe().
		if (!operator) operator = 'unlogged';

		return maySubscribe(operator, this.course, operator, role);
	}
});

Template.courseRole.events({
	'click .js-role-enroll-btn'(event, instance) {
		event.preventDefault();
		instance.enrolling.set(true);
	},

	'click .js-role-subscribe-btn'(event, instance) {
		event.preventDefault();

		const comment = instance.$('.js-comment').val();
		instance.busy('enrolling');
		instance.autorun((computation) => {
			const userId = Meteor.userId();
			if (userId) {
				computation.stop();
				Meteor.call('add_role', this.course._id, userId, this.roletype.type, err => {
					if (err) {
						console.error(err);
					} else {
						Meteor.call('change_comment', this.course._id, comment, err => {
							instance.busy(false);
							if (err) {
								console.error(err);
							} else {
								instance.enrolling.set(false);
							}
						});
					}
				});
			} else {
				Session.set('pleaseLogin', true);
				$('#accountTasks').modal('show');
			}
		});
	},

	'click .js-role-enroll-cancel': function (e, template) {
		template.enrolling.set(false);
		return false;
	},

	'click .js-role-unsubscribe-btn': function () {
		Meteor.call('remove_role', this.course._id, Meteor.userId(), this.roletype.type);
		return false;
	}
});
<|endoftext|>