<commit_before>'use strict';

/* global superagent:false */

var context = {};
context.fragments = {};

// *********************************************
//  Navbar Fragment
// *********************************************

function NavbarFragment (context) {
    var that = this;

    this.container = $('#box-navbar');
    this.buttonGetADouble = $('#get-a-double-button');
    this.buttonLogin = $('#login-button');
    this.buttonLogout = $('#logout-button');
    this.buttonSignup = $('#signup-button');
    this.buttonDeleteAccount = $('#delete-account-button');

    this.confirmDialog = $('#modal-account-delete');
    this.confirmDialogButtonOk = $('#modal-account-delete-ok');
    this.confirmDialogButtonCancel = $('#modal-account-delete-cancel');

    this.buttonGetADouble.click(function () {
        showFragment(context.fragments.signup);
    });

    this.buttonSignup.click(function () {
        showFragment(context.fragments.signup);
    });

    this.buttonLogin.click(function () {
        showFragment(context.fragments.login);
    });

    this.buttonLogout.click(function (event) {
        $.removeCookie('userToken');
        window.location.href = '/';
    });

    this.buttonDeleteAccount.click(function (event) {
        that.confirmDialog.modal('show');
    });

    this.confirmDialogButtonOk.click(function () {
        that.confirmDialog.modal('hide');

        superagent.post(window.BACKEND_URL + '/api/v1/users/signoff').withCredentials().query({ userToken: $.cookie('userToken') }).end(function (error, result) {
            if (error || !result.ok) {
                console.error('Failed to delete the account.', error, result && result.status);
                return;
            }

            $.removeCookie('userToken');
            window.location.href = '/';
        });
    });

    this.confirmDialogButtonCancel.click(function (event) {
        that.confirmDialog.modal('hide');
    });
}

NavbarFragment.prototype.show = function () {
    this.container.show();
};

NavbarFragment.prototype.hide = function () {
    this.container.hide();
};

// *********************************************
//  Welcome Fragment
// *********************************************

function WelcomeFragment (context) {
    this.context = context;
    this.container = $('#box-welcome');
}

WelcomeFragment.prototype.show = function () {
    this.container.show();
    this.context.navbar.buttonLogout.hide();
    this.context.navbar.buttonLogin.show();
    this.context.navbar.buttonSignup.show();
    this.context.navbar.buttonDeleteAccount.hide();
};

WelcomeFragment.prototype.hide = function () {
    this.container.hide();
};

// *********************************************
//  Login Fragment
// *********************************************

function LoginFragment (context) {
    var that = this;
    this.context = context;

    this.container = $('#box-login');
    this.form = $('#form-generate');
    this.email = $('#form-generate input[name=email]');
    this.password = $('#form-generate input[name=password]');
    this.remember = $('#form-generate input[name=remember]');

    this.form.submit(function (event) {
        event.preventDefault();

        superagent.post(window.BACKEND_URL + '/api/v1/users/login').withCredentials().auth(that.email.val(), that.password.val()).end(function (error, result) {
            if (error || !result.ok) {
                console.error('Failed to sign in.', error, result && result.status);
                that.password.val('');
                that.email.parent().addClass('has-error');
                that.password.parent().addClass('has-error');
                return;
            }

            $.cookie('userToken', result.body.userToken, { expires: (that.remember.is(':checked') ? 7 : null ) });
            that.email.parent().removeClass('has-error');
            that.password.parent().removeClass('has-error');

            getApplicationDetails(function (error, result) {
                if (error) {
                    console.error('Failed to get details.', error);
                    return;
                }

                context.fragments.application.fillForm(result.appKey, result.appSecret);
                showFragment(context.fragments.application);
            });
        });
    });
}

LoginFragment.prototype.show = function () {
    this.container.show();
    this.context.navbar.buttonSignup.show();
    this.context.navbar.buttonLogout.hide();
    this.context.navbar.buttonLogin.hide();
    this.context.navbar.buttonDeleteAccount.hide();
};

LoginFragment.prototype.hide = function () {
    this.container.hide();
};

// *********************************************
//  Signup Fragment
// *********************************************

function SignupFragment (context) {
    var that = this;
    this.context = context;

    this.container = $('#box-signup');
    this.form = $('#signup-form');
    this.email = $('#signup-form input[name=email]');
    this.password = $('#signup-form input[name=password]');
    this.passwordRepeat = $('#signup-form input[name=password-repeat]');

    this.form.submit(function (event) {
        event.preventDefault();

        if (that.password.val() !== that.passwordRepeat.val()) {
            console.error('Passwords don\'t match');
            that.passwordRepeat.val('');
            that.passwordRepeat.parent().addClass('has-error');
            return;
        }

        superagent.post(window.BACKEND_URL + '/api/v1/users/signup').auth(that.email.val(), that.password.val()).end(function (error, result) {
            if (error) {
                console.error('Unable to reach the server.', error);
                return;
            }

            if (result.status === 409) {
                console.error('User %s already exists.', that.email.val());
                that.email.parent().addClass('has-error');
                return;
            }

            if (!result.ok) {
                console.error('Unable to register new account "%s".', that.email.val());
                return;
            }

            console.log('Account for user %s created.', that.email.val());

            $.cookie('userToken', result.body.userToken);
            console.log(that.passwordRepeat);
            that.passwordRepeat.parent().removeClass('has-error');
            that.email.parent().removeClass('has-error');
            context.fragments.application.fillForm(result.body.appKey, result.body.appSecret);
            showFragment(context.fragments.application);
        });
    });
}

SignupFragment.prototype.show = function () {
    this.container.show();
    this.context.navbar.buttonSignup.hide();
    this.context.navbar.buttonLogout.hide();
    this.context.navbar.buttonLogin.show();
    this.context.navbar.buttonDeleteAccount.hide();
};

SignupFragment.prototype.hide = function () {
    this.container.hide();
};

// *********************************************
//  Application Fragment
// *********************************************

function ApplicationFragment (context) {
    var that = this;
    this.context = context;

    this.container = $('#box-application');
    this.confirmDialog = $('#modal-confirm');
    this.confirmDialogButtonOk = $('#modal-confirm-ok');
    this.confirmDialogButtonCancel = $('#modal-confirm-cancel');
    this.buttonRefresh = $('#app-refresh-button');
    this.inputAppKey = $('#application-key');
    this.inputAppSecret = $('#application-secret');
    this.inputCallbackUrl = $('#callback-url');
    this.usageAppKeyLabel = $('#usage-app-key');
    this.usageAppSecretLabel = $('#usage-app-secret');
    this.usageRemoteLabel = $('#usage-remote');
    this.usageExampleUrlLabel = $('#usage-example-url');

    this.buttonRefresh.click(function (event) {
        that.confirmDialog.modal('show');
    });

    this.confirmDialogButtonOk.click(function () {
        that.confirmDialog.modal('hide');

        superagent.post(window.BACKEND_URL + '/api/v1/apps/0/generate').withCredentials().query({ userToken: $.cookie('userToken') }).end(function (error, result) {
            if (error || !result.ok) {
                console.error('Failed to get details.', error);
                return;
            }

            that.fillForm(result.body.appKey, result.body.appSecret);
        });
    });

    this.confirmDialogButtonCancel.click(function (event) {
        that.confirmDialog.modal('hide');
    });
}

ApplicationFragment.prototype.show = function () {
    this.container.show();
    this.context.navbar.buttonSignup.hide();
    this.context.navbar.buttonLogout.show();
    this.context.navbar.buttonLogin.hide();
    this.context.navbar.buttonDeleteAccount.show();
};

ApplicationFragment.prototype.hide = function () {
    this.container.hide();
};

ApplicationFragment.prototype.fillForm = function (appKey, appSecret) {
    this.inputAppKey.val(appKey);
    this.inputAppKey.click(function () { $(this).select(); });
    this.inputAppSecret.val(appSecret);
    this.inputAppSecret.click(function () { $(this).select(); });
    this.inputCallbackUrl.text(window.BACKEND_URL + '/proxy/' + appKey);
    this.inputCallbackUrl.attr('href', window.BACKEND_URL + '/proxy/' + appKey + '/test/route');

    this.usageAppKeyLabel.text(appKey);
    this.usageAppSecretLabel.text(appSecret);
    this.usageRemoteLabel.text(window.BACKEND_URL);
    this.usageExampleUrlLabel.text(window.BACKEND_URL + '/proxy/' + appKey + '/test/route');
};

// some global helpers
function getApplicationDetails(callback) {
    var req = superagent.post(window.BACKEND_URL + '/api/v1/apps/0/details').withCredentials().query({ userToken: $.cookie('userToken') }).end(function (error, result) {
        if (error || !result.ok) {
            return callback(error ? error : result.status);
        }

        callback(null, result.body);
    });
}

function showFragment(fragment) {
    for (var frag in context.fragments) {
        if (context.fragments.hasOwnProperty(frag)) {
            context.fragments[frag].hide();
        }
    }

    fragment.show();
}

function init() {
    context.navbar = new NavbarFragment(context);
    context.fragments.welcome = new WelcomeFragment(context);
    context.fragments.login = new LoginFragment(context);
    context.fragments.signup = new SignupFragment(context);
    context.fragments.application = new ApplicationFragment(context);

    // figure out if we have a session and then carry on
    if ($.cookie('userToken')) {
        getApplicationDetails(function (error, result) {
            if (error) {
                console.error('Failed to get details.', error);
                showFragment(context.fragments.welcome);
                return;
            }

            context.fragments.application.fillForm(result.appKey, result.appSecret);
            showFragment(context.fragments.application);
        });
    } else {
        showFragment(context.fragments.welcome);
    }
}

window.addEventListener('load', init);
<commit_msg>Do not append silently /test/route to the callback base url<commit_after>'use strict';

/* global superagent:false */

var context = {};
context.fragments = {};

// *********************************************
//  Navbar Fragment
// *********************************************

function NavbarFragment (context) {
    var that = this;

    this.container = $('#box-navbar');
    this.buttonGetADouble = $('#get-a-double-button');
    this.buttonLogin = $('#login-button');
    this.buttonLogout = $('#logout-button');
    this.buttonSignup = $('#signup-button');
    this.buttonDeleteAccount = $('#delete-account-button');

    this.confirmDialog = $('#modal-account-delete');
    this.confirmDialogButtonOk = $('#modal-account-delete-ok');
    this.confirmDialogButtonCancel = $('#modal-account-delete-cancel');

    this.buttonGetADouble.click(function () {
        showFragment(context.fragments.signup);
    });

    this.buttonSignup.click(function () {
        showFragment(context.fragments.signup);
    });

    this.buttonLogin.click(function () {
        showFragment(context.fragments.login);
    });

    this.buttonLogout.click(function (event) {
        $.removeCookie('userToken');
        window.location.href = '/';
    });

    this.buttonDeleteAccount.click(function (event) {
        that.confirmDialog.modal('show');
    });

    this.confirmDialogButtonOk.click(function () {
        that.confirmDialog.modal('hide');

        superagent.post(window.BACKEND_URL + '/api/v1/users/signoff').withCredentials().query({ userToken: $.cookie('userToken') }).end(function (error, result) {
            if (error || !result.ok) {
                console.error('Failed to delete the account.', error, result && result.status);
                return;
            }

            $.removeCookie('userToken');
            window.location.href = '/';
        });
    });

    this.confirmDialogButtonCancel.click(function (event) {
        that.confirmDialog.modal('hide');
    });
}

NavbarFragment.prototype.show = function () {
    this.container.show();
};

NavbarFragment.prototype.hide = function () {
    this.container.hide();
};

// *********************************************
//  Welcome Fragment
// *********************************************

function WelcomeFragment (context) {
    this.context = context;
    this.container = $('#box-welcome');
}

WelcomeFragment.prototype.show = function () {
    this.container.show();
    this.context.navbar.buttonLogout.hide();
    this.context.navbar.buttonLogin.show();
    this.context.navbar.buttonSignup.show();
    this.context.navbar.buttonDeleteAccount.hide();
};

WelcomeFragment.prototype.hide = function () {
    this.container.hide();
};

// *********************************************
//  Login Fragment
// *********************************************

function LoginFragment (context) {
    var that = this;
    this.context = context;

    this.container = $('#box-login');
    this.form = $('#form-generate');
    this.email = $('#form-generate input[name=email]');
    this.password = $('#form-generate input[name=password]');
    this.remember = $('#form-generate input[name=remember]');

    this.form.submit(function (event) {
        event.preventDefault();

        superagent.post(window.BACKEND_URL + '/api/v1/users/login').withCredentials().auth(that.email.val(), that.password.val()).end(function (error, result) {
            if (error || !result.ok) {
                console.error('Failed to sign in.', error, result && result.status);
                that.password.val('');
                that.email.parent().addClass('has-error');
                that.password.parent().addClass('has-error');
                return;
            }

            $.cookie('userToken', result.body.userToken, { expires: (that.remember.is(':checked') ? 7 : null ) });
            that.email.parent().removeClass('has-error');
            that.password.parent().removeClass('has-error');

            getApplicationDetails(function (error, result) {
                if (error) {
                    console.error('Failed to get details.', error);
                    return;
                }

                context.fragments.application.fillForm(result.appKey, result.appSecret);
                showFragment(context.fragments.application);
            });
        });
    });
}

LoginFragment.prototype.show = function () {
    this.container.show();
    this.context.navbar.buttonSignup.show();
    this.context.navbar.buttonLogout.hide();
    this.context.navbar.buttonLogin.hide();
    this.context.navbar.buttonDeleteAccount.hide();
};

LoginFragment.prototype.hide = function () {
    this.container.hide();
};

// *********************************************
//  Signup Fragment
// *********************************************

function SignupFragment (context) {
    var that = this;
    this.context = context;

    this.container = $('#box-signup');
    this.form = $('#signup-form');
    this.email = $('#signup-form input[name=email]');
    this.password = $('#signup-form input[name=password]');
    this.passwordRepeat = $('#signup-form input[name=password-repeat]');

    this.form.submit(function (event) {
        event.preventDefault();

        if (that.password.val() !== that.passwordRepeat.val()) {
            console.error('Passwords don\'t match');
            that.passwordRepeat.val('');
            that.passwordRepeat.parent().addClass('has-error');
            return;
        }

        superagent.post(window.BACKEND_URL + '/api/v1/users/signup').auth(that.email.val(), that.password.val()).end(function (error, result) {
            if (error) {
                console.error('Unable to reach the server.', error);
                return;
            }

            if (result.status === 409) {
                console.error('User %s already exists.', that.email.val());
                that.email.parent().addClass('has-error');
                return;
            }

            if (!result.ok) {
                console.error('Unable to register new account "%s".', that.email.val());
                return;
            }

            console.log('Account for user %s created.', that.email.val());

            $.cookie('userToken', result.body.userToken);
            console.log(that.passwordRepeat);
            that.passwordRepeat.parent().removeClass('has-error');
            that.email.parent().removeClass('has-error');
            context.fragments.application.fillForm(result.body.appKey, result.body.appSecret);
            showFragment(context.fragments.application);
        });
    });
}

SignupFragment.prototype.show = function () {
    this.container.show();
    this.context.navbar.buttonSignup.hide();
    this.context.navbar.buttonLogout.hide();
    this.context.navbar.buttonLogin.show();
    this.context.navbar.buttonDeleteAccount.hide();
};

SignupFragment.prototype.hide = function () {
    this.container.hide();
};

// *********************************************
//  Application Fragment
// *********************************************

function ApplicationFragment (context) {
    var that = this;
    this.context = context;

    this.container = $('#box-application');
    this.confirmDialog = $('#modal-confirm');
    this.confirmDialogButtonOk = $('#modal-confirm-ok');
    this.confirmDialogButtonCancel = $('#modal-confirm-cancel');
    this.buttonRefresh = $('#app-refresh-button');
    this.inputAppKey = $('#application-key');
    this.inputAppSecret = $('#application-secret');
    this.inputCallbackUrl = $('#callback-url');
    this.usageAppKeyLabel = $('#usage-app-key');
    this.usageAppSecretLabel = $('#usage-app-secret');
    this.usageRemoteLabel = $('#usage-remote');
    this.usageExampleUrlLabel = $('#usage-example-url');

    this.buttonRefresh.click(function (event) {
        that.confirmDialog.modal('show');
    });

    this.confirmDialogButtonOk.click(function () {
        that.confirmDialog.modal('hide');

        superagent.post(window.BACKEND_URL + '/api/v1/apps/0/generate').withCredentials().query({ userToken: $.cookie('userToken') }).end(function (error, result) {
            if (error || !result.ok) {
                console.error('Failed to get details.', error);
                return;
            }

            that.fillForm(result.body.appKey, result.body.appSecret);
        });
    });

    this.confirmDialogButtonCancel.click(function (event) {
        that.confirmDialog.modal('hide');
    });
}

ApplicationFragment.prototype.show = function () {
    this.container.show();
    this.context.navbar.buttonSignup.hide();
    this.context.navbar.buttonLogout.show();
    this.context.navbar.buttonLogin.hide();
    this.context.navbar.buttonDeleteAccount.show();
};

ApplicationFragment.prototype.hide = function () {
    this.container.hide();
};

ApplicationFragment.prototype.fillForm = function (appKey, appSecret) {
    this.inputAppKey.val(appKey);
    this.inputAppKey.click(function () { $(this).select(); });
    this.inputAppSecret.val(appSecret);
    this.inputAppSecret.click(function () { $(this).select(); });
    this.inputCallbackUrl.text(window.BACKEND_URL + '/proxy/' + appKey + '/');
    this.inputCallbackUrl.attr('href', window.BACKEND_URL + '/proxy/' + appKey + '/');

    this.usageAppKeyLabel.text(appKey);
    this.usageAppSecretLabel.text(appSecret);
    this.usageRemoteLabel.text(window.BACKEND_URL);
    this.usageExampleUrlLabel.text(window.BACKEND_URL + '/proxy/' + appKey + '/test/route');
};

// some global helpers
function getApplicationDetails(callback) {
    var req = superagent.post(window.BACKEND_URL + '/api/v1/apps/0/details').withCredentials().query({ userToken: $.cookie('userToken') }).end(function (error, result) {
        if (error || !result.ok) {
            return callback(error ? error : result.status);
        }

        callback(null, result.body);
    });
}

function showFragment(fragment) {
    for (var frag in context.fragments) {
        if (context.fragments.hasOwnProperty(frag)) {
            context.fragments[frag].hide();
        }
    }

    fragment.show();
}

function init() {
    context.navbar = new NavbarFragment(context);
    context.fragments.welcome = new WelcomeFragment(context);
    context.fragments.login = new LoginFragment(context);
    context.fragments.signup = new SignupFragment(context);
    context.fragments.application = new ApplicationFragment(context);

    // figure out if we have a session and then carry on
    if ($.cookie('userToken')) {
        getApplicationDetails(function (error, result) {
            if (error) {
                console.error('Failed to get details.', error);
                showFragment(context.fragments.welcome);
                return;
            }

            context.fragments.application.fillForm(result.appKey, result.appSecret);
            showFragment(context.fragments.application);
        });
    } else {
        showFragment(context.fragments.welcome);
    }
}

window.addEventListener('load', init);
<|endoftext|>


<commit_before>import { all, spread } from 'axios';
import {
  fetchBurnedAreaGrouped,
  fetchMODISLatest,
  getAreaIntersectionGrouped,
} from 'services/analysis-cached';
import firesRanked from 'components/widgets/fires/fires-ranked';

import {
  POLITICAL_BOUNDARIES_DATASET,
  BURNED_AREA_MODIS_DATASET,
} from 'data/datasets';
import {
  DISPUTED_POLITICAL_BOUNDARIES,
  POLITICAL_BOUNDARIES,
  BURNED_AREA_MODIS,
} from 'data/layers';

import getWidgetProps from './selectors';

const defaultConfig = {
  widget: 'burnedAreaRanked',
  title: {
    default: 'Regions with the most burned area in {location}',
    global: 'Global regions with the most burned area',
  },
  categories: ['fires'],
  dataType: 'fires',
  large: true,
  types: ['global', 'country'],
  admins: ['global', 'adm0', 'adm1'],
  settingsConfig: [
    {
      key: 'forestType',
      label: 'Forest Type',
      type: 'select',
      placeholder: 'All land cover',
      clearable: true,
    },
    {
      key: 'landCategory',
      label: 'Land Category',
      type: 'select',
      placeholder: 'All categories',
      clearable: true,
      border: true,
    },
    {
      key: 'dataset',
      label: 'fires dataset',
      type: 'select',
    },
    {
      key: 'unit',
      label: 'unit',
      type: 'select',
      whitelist: ['total_burn', 'burn_proportion', 'significance'],
    },
    {
      key: 'weeks',
      label: 'show data for the last',
      type: 'select',
      whitelist: [4, 13, 26, 52],
      border: true,
      noSort: true,
    },
    {
      key: 'firesThreshold',
      label: 'canopy density',
      type: 'mini-select',
      metaKey: 'widget_canopy_density',
      noSort: true,
    },
  ],
  chartType: 'lollipop',
  colors: 'fires',
  sortOrder: {
    summary: 6,
    fires: 3,
    global: 100,
  },
  datasets: [
    {
      dataset: POLITICAL_BOUNDARIES_DATASET,
      layers: [DISPUTED_POLITICAL_BOUNDARIES, POLITICAL_BOUNDARIES],
      boundary: true,
    },
    // fires
    {
      dataset: BURNED_AREA_MODIS_DATASET,
      layers: [BURNED_AREA_MODIS],
    },
  ],
  refetchKeys: [
    'dataset',
    'forestType',
    'landCategory',
    'weeks',
    'firesThreshold',
  ],
  metaKey: 'umd_modis_burned_areas',
  sentences: {
    initial:
      'In the last {timeframe} in {location}, the region with the most {significant} burned area was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned in {location} and is {status} compared to the same period going back to 2001.',
    withInd:
      'In the last {timeframe} in {location}, the region with the most {significant} burned area within {indicator} was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned in {location} and is {status} compared to the same period going back to 2001.',
    densityInitial:
      'In the last {timeframe} in {location}, the region with the highest proportion of land area burned was {topRegion}, with {topRegionDensity}. This represents {topRegionPerc} of the total area burned in {location} in the same period.',
    densityWithInd:
      'In the last {timeframe} in {location}, the region with the highest proportion of land area burned within {indicator} was {topRegion}, with {topRegionDensity}. This represents {topRegionPerc} of the total area burned in {location} in the same period.',
    countsInitial:
      'In the last {timeframe} in {location}, the region with the most burned area was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    countsWithInd:
      'In the last {timeframe} in {location}, the region with the most burned area within {indicator} was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    initialGlobal:
      'In the last {timeframe}, the country with the most {significant} burned area globally was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} and is {status} compared to the same period going back to 2001.',
    withIndGlobal:
      'In the last {timeframe}, the country with the most {significant} burned area within {indicator} globally was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} and is {status} compared to the same period going back to 2001.',
    densityInitialGlobal:
      'In the last {timeframe}, the country with the highest proportion of land area burned globally was {topRegion}, with {topRegionDensity}. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    densityWithIndGlobal:
      'In the last {timeframe}, the country with the highest proportion of land area burned within {indicator} globally was {topRegion}, with {topRegionDensity}. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    countsInitialGlobal:
      'In the last {timeframe}, the country with the most burned area globally was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    countsWithIndGlobal:
      'In the last {timeframe}, the country with the most burned area within {indicator} globally was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} in the same periodd.',
  },
  settings: {
    unit: 'significance',
    pageSize: 5,
    page: 0,
    period: 'week',
    weeks: 4,
    dataset: 'modis_burned_area',
    layerStartDate: null,
    layerEndDate: null,
    firesThreshold: 0,
  },
  getData: (params) =>
    fetchMODISLatest(params)
      .then((response) => (response && response.date) || null)
      .then((latest) =>
        all([
          fetchBurnedAreaGrouped({ ...params, latest }),
          getAreaIntersectionGrouped(params),
        ])
          .then(
            spread((alerts, areas) => {
              const { data } = alerts.data;
              const area = areas.data && areas.data.data;
              return { alerts: data, latest, area } || {};
            })
          )
          .catch(() => {
            return null;
          })
      )
      .catch(() => {
        return null;
      }),
  getDataURL: async (params) => {
    const latestResponse = await fetchMODISLatest(params);
    const latest = (latestResponse && latestResponse.date) || null;

    return [
      fetchBurnedAreaGrouped({ ...params, latest, download: true }),
      getAreaIntersectionGrouped({ ...params, download: true }),
    ];
  },
  getWidgetProps,
};

export default {
  widget: 'burnedAreaRanked',
  proxy: true,
  refetchKeys: ['dataset'],
  getWidget: (widgetSettings) => {
    // called when settings changes
    if (!widgetSettings || !widgetSettings.dataset) {
      return defaultConfig;
    }
    if (widgetSettings.dataset !== 'modis_burned_area') {
      return firesRanked;
    }
    return defaultConfig;
  },
};
<commit_msg>Updated ranked sentence<commit_after>import { all, spread } from 'axios';
import {
  fetchBurnedAreaGrouped,
  fetchMODISLatest,
  getAreaIntersectionGrouped,
} from 'services/analysis-cached';
import firesRanked from 'components/widgets/fires/fires-ranked';

import {
  POLITICAL_BOUNDARIES_DATASET,
  BURNED_AREA_MODIS_DATASET,
} from 'data/datasets';
import {
  DISPUTED_POLITICAL_BOUNDARIES,
  POLITICAL_BOUNDARIES,
  BURNED_AREA_MODIS,
} from 'data/layers';

import getWidgetProps from './selectors';

const defaultConfig = {
  widget: 'burnedAreaRanked',
  title: {
    default: 'Regions with the most burned area in {location}',
    global: 'Global regions with the most burned area',
  },
  categories: ['fires'],
  dataType: 'fires',
  large: true,
  types: ['global', 'country'],
  admins: ['global', 'adm0', 'adm1'],
  settingsConfig: [
    {
      key: 'forestType',
      label: 'Forest Type',
      type: 'select',
      placeholder: 'All land cover',
      clearable: true,
    },
    {
      key: 'landCategory',
      label: 'Land Category',
      type: 'select',
      placeholder: 'All categories',
      clearable: true,
      border: true,
    },
    {
      key: 'dataset',
      label: 'fires dataset',
      type: 'select',
    },
    {
      key: 'unit',
      label: 'unit',
      type: 'select',
      whitelist: ['total_burn', 'burn_proportion', 'significance'],
    },
    {
      key: 'weeks',
      label: 'show data for the last',
      type: 'select',
      whitelist: [4, 13, 26, 52],
      border: true,
      noSort: true,
    },
    {
      key: 'firesThreshold',
      label: 'canopy density',
      type: 'mini-select',
      metaKey: 'widget_canopy_density',
      noSort: true,
    },
  ],
  chartType: 'lollipop',
  colors: 'fires',
  sortOrder: {
    summary: 6,
    fires: 3,
    global: 100,
  },
  datasets: [
    {
      dataset: POLITICAL_BOUNDARIES_DATASET,
      layers: [DISPUTED_POLITICAL_BOUNDARIES, POLITICAL_BOUNDARIES],
      boundary: true,
    },
    // fires
    {
      dataset: BURNED_AREA_MODIS_DATASET,
      layers: [BURNED_AREA_MODIS],
    },
  ],
  refetchKeys: [
    'dataset',
    'forestType',
    'landCategory',
    'weeks',
    'firesThreshold',
  ],
  metaKey: 'umd_modis_burned_areas',
  sentences: {
    initial:
      'In the most recent {timeframe} of data in {location}, the region with the most {significant} burned area was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned in {location} and is {status} compared to the same period going back to 2001.',
    withInd:
      'In the most recent {timeframe} of data in {location}, the region with the most {significant} burned area within {indicator} was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned in {location} and is {status} compared to the same period going back to 2001.',
    densityInitial:
      'In the most recent {timeframe} of data in {location}, the region with the highest proportion of land area burned was {topRegion}, with {topRegionDensity}. This represents {topRegionPerc} of the total area burned in {location} in the same period.',
    densityWithInd:
      'In the most recent {timeframe} of data in {location}, the region with the highest proportion of land area burned within {indicator} was {topRegion}, with {topRegionDensity}. This represents {topRegionPerc} of the total area burned in {location} in the same period.',
    countsInitial:
      'In the most recent {timeframe} of data in {location}, the region with the most burned area was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    countsWithInd:
      'In the most recent {timeframe} of data in {location}, the region with the most burned area within {indicator} was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    initialGlobal:
      'In the most recent {timeframe} of data, the country with the most {significant} burned area globally was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} and is {status} compared to the same period going back to 2001.',
    withIndGlobal:
      'In the most recent {timeframe} of data, the country with the most {significant} burned area within {indicator} globally was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} and is {status} compared to the same period going back to 2001.',
    densityInitialGlobal:
      'In the most recent {timeframe} of data, the country with the highest proportion of land area burned globally was {topRegion}, with {topRegionDensity}. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    densityWithIndGlobal:
      'In the most recent {timeframe} of data, the country with the highest proportion of land area burned within {indicator} globally was {topRegion}, with {topRegionDensity}. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    countsInitialGlobal:
      'In the most recent {timeframe} of data, the country with the most burned area globally was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} in the same period.',
    countsWithIndGlobal:
      'In the most recent {timeframe} of data, the country with the most burned area within {indicator} globally was {topRegion}, with {topRegionCount} land area burned. This represents {topRegionPerc} of the total area burned {location} in the same periodd.',
  },
  settings: {
    unit: 'significance',
    pageSize: 5,
    page: 0,
    period: 'week',
    weeks: 4,
    dataset: 'modis_burned_area',
    layerStartDate: null,
    layerEndDate: null,
    firesThreshold: 0,
  },
  getData: (params) =>
    fetchMODISLatest(params)
      .then((response) => (response && response.date) || null)
      .then((latest) =>
        all([
          fetchBurnedAreaGrouped({ ...params, latest }),
          getAreaIntersectionGrouped(params),
        ])
          .then(
            spread((alerts, areas) => {
              const { data } = alerts.data;
              const area = areas.data && areas.data.data;
              return { alerts: data, latest, area } || {};
            })
          )
          .catch(() => {
            return null;
          })
      )
      .catch(() => {
        return null;
      }),
  getDataURL: async (params) => {
    const latestResponse = await fetchMODISLatest(params);
    const latest = (latestResponse && latestResponse.date) || null;

    return [
      fetchBurnedAreaGrouped({ ...params, latest, download: true }),
      getAreaIntersectionGrouped({ ...params, download: true }),
    ];
  },
  getWidgetProps,
};

export default {
  widget: 'burnedAreaRanked',
  proxy: true,
  refetchKeys: ['dataset'],
  getWidget: (widgetSettings) => {
    // called when settings changes
    if (!widgetSettings || !widgetSettings.dataset) {
      return defaultConfig;
    }
    if (widgetSettings.dataset !== 'modis_burned_area') {
      return firesRanked;
    }
    return defaultConfig;
  },
};
<|endoftext|>


<commit_before>import {clone, pipeP, map} from 'ramda'
import {printRequest} from 'apollo-client/transport/networkInterface'
import {Socket as PhoenixSocket} from 'phoenix';

function applyWares (ctx, wares) {
  return new Promise(function (resolve, reject) {
    const queue = function (funcs, scope) {
      const next = function () {
        if (funcs.length > 0) {
          const f = funcs.shift()
          f && (f.applyMiddleware || f.applyAfterware).apply(scope, [ctx, next])
        }
        else {
          resolve(ctx)
        }
      }
      next()
    }
    queue(wares.slice())
  })
}

function executeQuery(sockets, context) {
  const {request, options} = context
  const {uri, channel} = options
  const Socket = options.Socket || PhoenixSocket

  if (! uri) throw "Missing options.uri"
  if (! channel) throw "Missing options.channel"
  if (! channel.topic) throw "Missing options.channel.topic"

  if (options.logger === true) {
    options.logger = (kind, msg, data) =>
      console.log(`Apollo websocket ${kind}: ${msg}`, data)
  }

  if(! sockets[uri]) {
    sockets[uri] = {
      conn: new Socket(uri, options),
      channels: {}
    }
  }

  const socket = sockets[uri]
  let chan = socket.channels[channel.topic]

  function joinChannel(resolve) {
    if (! socket.conn.isConnected()) {
      return socket.conn.connect()
    }
    if (! chan.conn) {
      chan.conn = socket.conn.channel(channel.topic, channel.params || {})
    }
    if (!chan.conn.isJoined() && !chan.conn.isJoining()) {
      chan.conn.join().receive("ok", _ => {
        const queue = chan.queue
        chan.queue = []
        map(performQuery, queue)
      }).receive('error', err => {
        chan.conn.leave()
        chan.conn = null
        chan.queue.pop()
        resolve(err)
      })
    }
  }

  function performQuery ({context, resolve, reject}) {
    const msg = context.options.channel.in_msg || "gql"
    const payload = printRequest(context.request)
    chan.conn.push(msg, payload)
      .receive("ok", resolve)
      .receive("error", resolve)
      .receive("timeout", reject.bind(null, 'timeout'))
  }

  return new Promise(function (resolve, reject) {
    if(! chan) {
      chan = socket.channels[channel.topic] = {
        conn: null,
        queue: []
      }
      socket.conn.onOpen(joinChannel.bind(null, resolve))
      socket.conn.onError(err => {
        if (chan.conn && (chan.conn.isJoined() || chan.conn.isJoining()) {
          chan.conn.leave()
        }
        chan.queue = []
        resolve(err)
      })
    }
    if (chan.conn && chan.conn.isJoined()) {
      performQuery({context, resolve, reject})
    } else {
      chan.queue.push({context, resolve, reject})
      joinChannel(resolve)
    }
  })
}

const responseData = ({response}) => {
  return new Promise(function (resolve, reject) {
    if (response.error){
      reject(response)
    } else if (response.data) {
      resolve(response)
    } else {
      reject('No response')
    }
  })
}

export function createNetworkInterface(ifaceOpts) {
  const sockets = {}
  const middlewares = []
  const afterwares = []

  const use = map(item => middlewares.push(item))
  const useAfter = map(item => afterwares.push(item))

  const requestMiddleware = (request) => {
    const options = clone(ifaceOpts)
    return applyWares({request, options}, middlewares)
  }

  const responseMiddleware = (response) => {
    const options = clone(ifaceOpts)
    return applyWares({response, options}, afterwares)
  }

  const query = pipeP(requestMiddleware,
                      executeQuery.bind(null, sockets),
                      responseMiddleware,
                      responseData)

  return {query, use, useAfter}
}
<commit_msg>Fix typo<commit_after>import {clone, pipeP, map} from 'ramda'
import {printRequest} from 'apollo-client/transport/networkInterface'
import {Socket as PhoenixSocket} from 'phoenix';

function applyWares (ctx, wares) {
  return new Promise(function (resolve, reject) {
    const queue = function (funcs, scope) {
      const next = function () {
        if (funcs.length > 0) {
          const f = funcs.shift()
          f && (f.applyMiddleware || f.applyAfterware).apply(scope, [ctx, next])
        }
        else {
          resolve(ctx)
        }
      }
      next()
    }
    queue(wares.slice())
  })
}

function executeQuery(sockets, context) {
  const {request, options} = context
  const {uri, channel} = options
  const Socket = options.Socket || PhoenixSocket

  if (! uri) throw "Missing options.uri"
  if (! channel) throw "Missing options.channel"
  if (! channel.topic) throw "Missing options.channel.topic"

  if (options.logger === true) {
    options.logger = (kind, msg, data) =>
      console.log(`Apollo websocket ${kind}: ${msg}`, data)
  }

  if(! sockets[uri]) {
    sockets[uri] = {
      conn: new Socket(uri, options),
      channels: {}
    }
  }

  const socket = sockets[uri]
  let chan = socket.channels[channel.topic]

  function joinChannel(resolve) {
    if (! socket.conn.isConnected()) {
      return socket.conn.connect()
    }
    if (! chan.conn) {
      chan.conn = socket.conn.channel(channel.topic, channel.params || {})
    }
    if (!chan.conn.isJoined() && !chan.conn.isJoining()) {
      chan.conn.join().receive("ok", _ => {
        const queue = chan.queue
        chan.queue = []
        map(performQuery, queue)
      }).receive('error', err => {
        chan.conn.leave()
        chan.conn = null
        chan.queue.pop()
        resolve(err)
      })
    }
  }

  function performQuery ({context, resolve, reject}) {
    const msg = context.options.channel.in_msg || "gql"
    const payload = printRequest(context.request)
    chan.conn.push(msg, payload)
      .receive("ok", resolve)
      .receive("error", resolve)
      .receive("timeout", reject.bind(null, 'timeout'))
  }

  return new Promise(function (resolve, reject) {
    if(! chan) {
      chan = socket.channels[channel.topic] = {
        conn: null,
        queue: []
      }
      socket.conn.onOpen(joinChannel.bind(null, resolve))
      socket.conn.onError(err => {
        if (chan.conn && (chan.conn.isJoined() || chan.conn.isJoining())) {
          chan.conn.leave()
        }
        chan.queue = []
        resolve(err)
      })
    }
    if (chan.conn && chan.conn.isJoined()) {
      performQuery({context, resolve, reject})
    } else {
      chan.queue.push({context, resolve, reject})
      joinChannel(resolve)
    }
  })
}

const responseData = ({response}) => {
  return new Promise(function (resolve, reject) {
    if (response.error){
      reject(response)
    } else if (response.data) {
      resolve(response)
    } else {
      reject('No response')
    }
  })
}

export function createNetworkInterface(ifaceOpts) {
  const sockets = {}
  const middlewares = []
  const afterwares = []

  const use = map(item => middlewares.push(item))
  const useAfter = map(item => afterwares.push(item))

  const requestMiddleware = (request) => {
    const options = clone(ifaceOpts)
    return applyWares({request, options}, middlewares)
  }

  const responseMiddleware = (response) => {
    const options = clone(ifaceOpts)
    return applyWares({response, options}, afterwares)
  }

  const query = pipeP(requestMiddleware,
                      executeQuery.bind(null, sockets),
                      responseMiddleware,
                      responseData)

  return {query, use, useAfter}
}
<|endoftext|>


<commit_before>const nonEmpty = x => !(x === undefined || x === null);

const isString = x => nonEmpty(x) && typeof x.valueOf() === 'string';

export const nullable = (boolean) => {
  if (boolean === false) return x => nonEmpty(x);
  else return () => true;
};

export const blank = (boolean) => {
  if (boolean === false) return x => isString(x) && x.length > 0;
  else return x => isString(x);
};

export const inList = (array) => {
  const msg = 'inList constraint requires a non-empty Array parameter';
  if (!nonEmpty(array) || array.constructor !== Array) throw new Error(msg);
  else return x => array.indexOf(x) > -1;
};

export const maxInclusive = (value) => {
  return x => x <= value;
};

export const minInclusive = (value) => {
  return x => x >= value;
};

export const maxExclusive = (value) => {
  return x => x < value;
};

export const minExclusive = (value) => {
  return x => x > value;
};

const rangeExp = /(?=\d+<?\.{2}<?\d+)/;

const isRangeLike = (value) => {
  return value.match(rangeExp) !== null;
};

export const size = (range) => {
  const msg1 = 'size constraint requires String parameter';
  if (!isString(range)) throw new Error(msg1);
  const msg2 = 'size constraint requires parameter to conform to Groovy range';
  if (!isRangeLike(range)) throw new Error(msg2);
};
<commit_msg>Reformat<commit_after>const nonEmpty = x => !(x === undefined || x === null);

const isString = x => nonEmpty(x) && typeof x.valueOf() === 'string';

export const nullable = (boolean) => {
  if (boolean === false) return x => nonEmpty(x);
  else return () => true;
};

export const blank = (boolean) => {
  if (boolean === false) return x => isString(x) && x.length > 0;
  else return x => isString(x);
};

export const inList = (array) => {
  const msg = 'inList constraint requires a non-empty Array parameter';
  if (!nonEmpty(array) || array.constructor !== Array) throw new Error(msg);
  else return x => array.indexOf(x) > -1;
};

export const maxInclusive = (value) => {
  return x => x <= value;
};

export const minInclusive = (value) => {
  return x => x >= value;
};

export const maxExclusive = (value) => {
  return x => x < value;
};

export const minExclusive = (value) => {
  return x => x > value;
};

const RANGE_REGEX = /(?=\d+<?\.{2}<?\d+)/;

const isRangeLike = (value) => {
  return value.match(RANGE_REGEX) !== null;
};

export const size = (range) => {
  const msg1 = 'size constraint requires String parameter';
  if (!isString(range)) throw new Error(msg1);
  const msg2 = 'size constraint requires parameter to conform to Groovy range';
  if (!isRangeLike(range)) throw new Error(msg2);
};
<|endoftext|>


<commit_before>import Promise from 'bluebird';
import logger from 'winston';
import chalk from 'chalk';
import {
  table,
  getBorderCharacters
} from 'table';

logger.cli();

const loadedPlugins = {};
const pendingDependencies = {};
const registeredServices = {};

const addPendingDependencies = dependencies => {
  for (let dependency of dependencies) {
    pendingDependencies[dependency] = Promise.pending();
  }
}

const loadService = async(root, path, serviceName, service) => {
  if (typeof service === 'string') {
    // shorthand for service declaration
    const frags = service.split('<<');
    const moduleName = frags[0];
    service = {
      module: moduleName.trim()
    }
    if (frags.length >= 2) {
      let depNames = frags[1].trim();
      service.require = depNames.split(',').map(name => name.trim());
    }
    if (frags.length >= 3) {
      const async = (frags[2].trim() == 'true');
      service.async = async;
    }
  }

  if (!service.func) {
    if (service.module) {
      service.func = root.require(`${path.replace(/\/$/, "")}/${service.module.replace(/^\//, "")}`);
      if (service.func.default) {
        // ES6 export default
        service.func = service.func.default;
      }
    } else {
      return;
    }
  }

  if (!service.func || typeof service.func !== "function") {
    return;
  }

  const requirementPromises = [];
  registeredServices[serviceName].requirements = [];

  if (Array.isArray(service.require)) {
    const requirements = service.require;
    for (let requirement of requirements) {
      if (typeof requirement === 'string' && requirement.startsWith('::')) {
        // shorthand for requirement.lib
        requirement = {
          lib: requirement.substring(2)
        };
      }
      registeredServices[serviceName].requirements.push(requirement);

      if (requirement.lib) {
        // node modules and modules that is local to root
        requirementPromises.push(Promise.resolve(root.require(requirement.lib)));
      } else {
        if (pendingDependencies[requirement]) {
          requirementPromises.push(pendingDependencies[requirement].promise);
        } else {
          registeredServices[serviceName].status = 'unresolvable';
          logger.error('Service Loader', `\tUnmet dependency: "${serviceName}" requires "${requirement}" but "${requirement}" cannot be found by the container.`);
          return;
        }
      }
    }
  }

  const requirements = await Promise.all(requirementPromises);

  logger.info('Service Loader', `\tRequirements for service "${serviceName}" resolved.`);

  registeredServices[serviceName].status = 'resolved';

  let result = service.func.apply({}, requirements);

  if (service.async) {
    result = await result;
  }

  registeredServices[serviceName].status = 'ready';

  if (pendingDependencies[serviceName]) {
    pendingDependencies[serviceName].resolve(result);
    logger.info('Service Loader', `\tService "${serviceName}" resolved as a dependency.`);
  }
}

const getStatusString = status => {
  switch (status) {
    case 'pending':
      return chalk.bgYellow.bold(status);
    case 'resolved':
    case 'ready':
      return chalk.bgGreen.black.bold(status);
    case 'error':
    case 'unresolvable':
      return chalk.bgRed.white.bold(status);
  }
}

const getServiceNameString = dep => {
  if (dep.lib) {
    return chalk.bold.grey(`::${dep.lib}`);
  }

  if(!registeredServices[dep]) {
    return chalk.red.bold(dep);
  }

  const status = registeredServices[dep].status;
  switch (status) {
    case 'pending':
      return chalk.bgYellow.bold(dep);
    case 'resolved':
    case 'ready':
      return chalk.bgGreen.black.bold(dep);
    case 'error':
    case 'unresolvable':
      return chalk.bgRed.white.bold(dep);
  }
}

const getServiceTable = services => {
  const data = [
    [
      chalk.bold('Name'),
      '',
      chalk.bold('Requirements'),
      chalk.bold('Status')
    ]
  ];

  for (let serviceName of services) {
    data.push([
      serviceName, !!pendingDependencies[serviceName] ? chalk.green.bold('E') : '',
      registeredServices[serviceName].requirements.map(dep => getServiceNameString(dep)).join(', '),
      getStatusString(registeredServices[serviceName].status)
    ]);
  }

  return table(data, {
    border: getBorderCharacters('ramac'),
    columns: {
      0: {
        width: 20
      },
      1: {
        width: 1
      },
      2: {
        width: 20
      },
      4: {
        width: 20
      }
    }
  });
}

const printTables = () => {
  for (let pluginName in loadedPlugins) {
    let pluginInfo = loadedPlugins[pluginName];
    console.log(`# Plugin "${pluginName}" \tat path "${pluginInfo.path}"`);
    let services = pluginInfo.services;
    console.log(getServiceTable(services));
  }
}

const enableKeyPress = () => {
  const stdin = process.stdin;
  stdin.setRawMode(true);
  stdin.resume();
  stdin.setEncoding('utf8');

  stdin.on('data', function(key) {
    if (key === '\u0003' || key === 'q') {
      process.exit();
    }

    if (key === '`') {
      printTables();
      return;
    }

    process.stdout.write(key);
  });

}

export const load = (root, configFilePath, interactive) => {
  const pluginPaths = root.require(configFilePath);

  const pluginConfigs = {};

  pluginPaths.map(path => {
    let pluginConfig = root.require(path);
    if (pluginConfig.default) {
      pluginConfig = pluginConfig.default;
    }
    pluginConfigs[path] = pluginConfig;
    logger.info(pluginConfig.name, `\tLoading module ${pluginConfig.name} at path ${path}`);
    loadedPlugins[pluginConfig.name] = {
      path
    }
  });

  for (let path in pluginConfigs) {
    const pluginConfig = pluginConfigs[path];

    if (Array.isArray(pluginConfig.exports)) {
      logger.info(pluginConfig.name, `\tExporting services from module ${pluginConfig.name}: ${pluginConfig.exports.join(', ')}`);
      addPendingDependencies(pluginConfig.exports);
    }
  };

  for (let path in pluginConfigs) {
    const pluginConfig = pluginConfigs[path];
    loadedPlugins[pluginConfig.name].services = [];
    const services = pluginConfig.services || {};
    logger.info(pluginConfig.name, `\tInitializing services for module ${pluginConfig.name}`)
    for (let serviceName in services) {
      logger.info(pluginConfig.name, `\t\tService name: ${serviceName}`);
      loadedPlugins[pluginConfig.name].services.push(serviceName);
      registeredServices[serviceName] = {
        status: 'pending'
      };
      loadService(root, path, serviceName, services[serviceName]).catch(e => {
        registeredServices[serviceName].status = 'error';
        logger.error('Service Loader', `\tService ${serviceName} failed to load!`, e)
      });
    }
  };

  if (interactive) {
    enableKeyPress();
  }
};

export const get = (name) => {
  return pendingDependencies[name].promise;
}<commit_msg>cleanup a little bit<commit_after>import Promise from 'bluebird';
import logger from 'winston';
import chalk from 'chalk';
import {
  table,
  getBorderCharacters
} from 'table';

logger.cli();

const loadedPlugins = {};
const pendingDependencies = {};
const registeredServices = {};

const addPendingDependencies = dependencies => {
  for (let dependency of dependencies) {
    pendingDependencies[dependency] = Promise.pending();
  }
}

const loadService = async(root, path, serviceName, service) => {
  if (typeof service === 'string') {
    // shorthand for service declaration
    const frags = service.split('<<');
    const moduleName = frags[0];
    service = {
      module: moduleName.trim()
    }
    if (frags.length >= 2) {
      let depNames = frags[1].trim();
      service.require = depNames.split(',').map(name => name.trim());
    }
    if (frags.length >= 3) {
      const async = (frags[2].trim() == 'true');
      service.async = async;
    }
  }

  if (!service.func) {
    if (service.module) {
      service.func = root.require(`${path.replace(/\/$/, "")}/${service.module.replace(/^\//, "")}`);
      if (service.func.default) {
        // ES6 export default
        service.func = service.func.default;
      }
    } else {
      return;
    }
  }

  if (!service.func || typeof service.func !== "function") {
    return;
  }

  const requirementPromises = [];
  const serviceDetail = registeredServices[serviceName];
  serviceDetail.requirements = [];

  if (Array.isArray(service.require)) {
    const requirements = service.require;
    for (let requirement of requirements) {
      if (typeof requirement === 'string' && requirement.startsWith('::')) {
        // shorthand for requirement.lib
        requirement = {
          lib: requirement.substring(2)
        };
      }
      serviceDetail.requirements.push(requirement);

      if (requirement.lib) {
        // node modules and modules that is local to root
        requirementPromises.push(Promise.resolve(root.require(requirement.lib)));
      } else {
        if (pendingDependencies[requirement]) {
          requirementPromises.push(pendingDependencies[requirement].promise);
        } else {
          serviceDetail.status = 'unresolvable';
          logger.error('Service Loader', `\tUnmet dependency: "${serviceName}" requires "${requirement}" but "${requirement}" cannot be found by the container.`);
          return;
        }
      }
    }
  }

  const requirements = await Promise.all(requirementPromises);

  logger.info('Service Loader', `\tRequirements for service "${serviceName}" resolved.`);

  serviceDetail.status = 'resolved';

  let result = service.func.apply({}, requirements);

  if (service.async) {
    result = await result;
  }

  serviceDetail.status = 'ready';

  if (pendingDependencies[serviceName]) {
    pendingDependencies[serviceName].resolve(result);
    logger.info('Service Loader', `\tService "${serviceName}" resolved as a dependency.`);
  }
}

const getStatusString = status => {
  switch (status) {
    case 'pending':
      return chalk.bgYellow.bold(status);
    case 'resolved':
    case 'ready':
      return chalk.bgGreen.bold(status);
    case 'error':
    case 'unresolvable':
      return chalk.bgRed.bold(status);
  }
}

const getServiceNameString = dep => {
  if (dep.lib) {
    return chalk.bold.grey(`::${dep.lib}`);
  }

  if(!registeredServices[dep]) {
    return chalk.red.bold(dep);
  }

  const status = registeredServices[dep].status;
  switch (status) {
    case 'pending':
      return chalk.yellow.bold(dep);
    case 'resolved':
    case 'ready':
      return chalk.green.bold(dep);
    case 'error':
    case 'unresolvable':
      return chalk.red.bold(dep);
  }
}

const getServiceTable = services => {
  const data = [
    [
      chalk.bold('Name'),
      '',
      chalk.bold('Requirements'),
      chalk.bold('Status')
    ]
  ];

  for (let serviceName of services) {
    data.push([
      serviceName, !!pendingDependencies[serviceName] ? chalk.green.bold('E') : '',
      registeredServices[serviceName].requirements.map(dep => getServiceNameString(dep)).join(', '),
      getStatusString(registeredServices[serviceName].status)
    ]);
  }

  return table(data, {
    border: getBorderCharacters('ramac'),
    columns: {
      0: {
        width: 20
      },
      1: {
        width: 1
      },
      2: {
        width: 20
      },
      4: {
        width: 20
      }
    }
  });
}

const printTables = () => {
  for (let pluginName in loadedPlugins) {
    let pluginInfo = loadedPlugins[pluginName];
    console.log(`# Plugin "${pluginName}" \tat path "${pluginInfo.path}"`);
    let services = pluginInfo.services;
    console.log(getServiceTable(services));
  }
}

const enableKeyPress = () => {
  const stdin = process.stdin;
  stdin.setRawMode(true);
  stdin.resume();
  stdin.setEncoding('utf8');

  stdin.on('data', function(key) {
    if (key === '\u0003' || key === 'q') {
      process.exit();
    }

    if (key === '`') {
      printTables();
      return;
    }

    process.stdout.write(key);
  });

}

export const load = (root, configFilePath, interactive) => {
  const pluginPaths = root.require(configFilePath);

  const pluginConfigs = {};

  pluginPaths.map(path => {
    let pluginConfig = root.require(path);
    if (pluginConfig.default) {
      pluginConfig = pluginConfig.default;
    }
    pluginConfigs[path] = pluginConfig;
    logger.info(pluginConfig.name, `\tLoading module ${pluginConfig.name} at path ${path}`);
    loadedPlugins[pluginConfig.name] = {
      path
    }
  });

  for (let path in pluginConfigs) {
    const pluginConfig = pluginConfigs[path];

    if (Array.isArray(pluginConfig.exports)) {
      logger.info(pluginConfig.name, `\tExporting services from module ${pluginConfig.name}: ${pluginConfig.exports.join(', ')}`);
      addPendingDependencies(pluginConfig.exports);
    }
  };

  for (let path in pluginConfigs) {
    const pluginConfig = pluginConfigs[path];
    loadedPlugins[pluginConfig.name].services = [];
    const services = pluginConfig.services || {};
    logger.info(pluginConfig.name, `\tInitializing services for module ${pluginConfig.name}`)
    for (let serviceName in services) {
      logger.info(pluginConfig.name, `\t\tService name: ${serviceName}`);
      loadedPlugins[pluginConfig.name].services.push(serviceName);
      registeredServices[serviceName] = {
        status: 'pending'
      };
      loadService(root, path, serviceName, services[serviceName]).catch(e => {
        registeredServices[serviceName].status = 'error';
        logger.error('Service Loader', `\tService ${serviceName} failed to load!`, e)
      });
    }
  };

  if (interactive) {
    enableKeyPress();
  }
};

export const get = (name) => {
  return pendingDependencies[name].promise;
}<|endoftext|>


<commit_before>/**
 * @file react-anything-sortable
 * @author jasonslyvia
 */

/*eslint new-cap:0, consistent-return: 0 */

'use strict';

/**
 * @dependency
 */
import React from 'react';
import ReactDOM from 'react-dom';
import {on, off, isFunction, isNumeric, position, closest, get,
        assign} from './utils';
import SortableItemMixin from './SortableItemMixin';

/**
 * @class Sortable
 */
const Sortable = React.createClass({
  propTypes: {
    /**
     * callback fires after sort operation finish
     * function (dataSet){
     *   //dataSet sorted
     * }
     */
    onSort: React.PropTypes.func,
    className: React.PropTypes.string
  },

  getInitialState() {
    //keep tracking the dimension and coordinates of all children
    this._dimensionArr = this.props.children ?
                         ( Array.isArray(this.props.children) ? this.props.children.map(() => {return {}; }) : [{}] ) :
                         [];

    //keep tracking the order of all children
    this._orderArr = [];
    let i = 0;
    while(i < this._dimensionArr.length){
      this._orderArr.push(i++);
    }

    return {
      isDragging: false,
      placeHolderIndex: null,
      left: null,
      top: null
    };
  },

  componentDidMount() {
    this.containerWidth = ReactDOM.findDOMNode(this).offsetWidth;
  },

  componentWillUnmount() {
    this.unbindEvent();
  },

  bindEvent() {
    //so that the focus won't be lost if cursor moving too fast
    this.__mouseMoveHandler = (e) => {
      /**
       * Since Chrome may trigger redundant mousemove event evne if
       * we didn't really move the mouse, we should make sure that
       * mouse coordinates really changed then respond to mousemove
       * event
       * @see https://code.google.com/p/chromium/issues/detail?id=327114
       */
      if (((e.pageX || e.clientX) === this._prevX && (e.pageY || e.clientY) === this._prevY) ||
          (this._prevX === null && this._prevY === null)) {
        return false;
      }

      this.handleMouseMove.call(this, e);
    };

    this.__mouseUpHandler = (e) => {
      this.handleMouseUp.call(this, e);
    };

    this.__touchMoveHandler = (e) => {
      this.handleMouseMove.call(this, {
        target: e.target,
        clientX: e.touches[0].clientX,
        clientY: e.touches[0].clientY,
        pageX: e.touches[0].pageX,
        pageY: e.touches[0].pageY
      });
    };

    this.__touchEndOrCancelHandler = (e) => {
      this.handleMouseUp.call(this, e);
    };

    if (!!('ontouchstart' in window)) {
      on(document, 'touchmove', this.__touchMoveHandler);
      on(document, 'touchend', this.__touchEndOrCancelHandler);
      on(document, 'touchcancel', this.__touchEndOrCancelHandler);
    }
    else {
      on(document, 'mousemove', this.__mouseMoveHandler);
      on(document, 'mouseup', this.__mouseUpHandler);
    }
  },

  unbindEvent() {
    if (!!('ontouchstart' in window)) {
      off(document, 'touchmove', this.__touchMoveHandler);
      off(document, 'touchend', this.__touchEndOrCancelHandler);
      off(document, 'touchcancel', this.__touchEndOrCancelHandler);
    }
    else {
      off(document, 'mousemove', this.__mouseMoveHandler);
      off(document, 'mouseup', this.__mouseUpHandler);
    }

    this.__mouseMoveHandler = null;
    this.__mouseUpHandler = null;
    this.__touchMoveHandler = null;
    this.__touchEndOrCancelHandler = null;
  },

  /**
   * getting ready for dragging
   * @param  {object} e     React event
   * @param  {numbner} index index of pre-dragging item
   */
  handleMouseDown(e, index) {
    this._draggingIndex = index;
    this._prevX = (e.pageX || e.clientX);
    this._prevY = (e.pageY || e.clientY);
    this._initOffset = e.offset;
    this._isReadyForDragging = true;
    this._hasInitDragging = false;

    //start listening mousemove and mouseup
    this.bindEvent();
  },

  /**
   * `add` a dragging item and re-calculating position of placeholder
   * @param  {object} e     React event
   */
  handleMouseMove(e) {
    this._isMouseMoving = true;

    if (!this._isReadyForDragging) {
      return false;
    }

    if (!this._hasInitDragging) {
      this._dimensionArr[this._draggingIndex].isPlaceHolder = true;
      this._hasInitDragging = false;
    }

    const newOffset = this.calculateNewOffset(e);
    const newIndex = this.calculateNewIndex(e);

    this._draggingIndex = newIndex;

    this.setState({
      isDragging: true,
      top: newOffset.top,
      left: newOffset.left,
      placeHolderIndex: newIndex
    });

    this._prevX = (e.pageX || e.clientX);
    this._prevY = (e.pageY || e.clientY);
  },

  /**
   * replace placeholder with dragging item
   * @param  {object} e     React event
   */
  handleMouseUp() {
    const _hasMouseMoved = this._isMouseMoving;
    this.unbindEvent();

    //reset temp lets
    this._draggingIndex = null;
    this._isReadyForDragging = false;
    this._isMouseMoving = false;
    this._initOffset = null;
    this._prevX = null;
    this._prevY = null;

    if (this.state.isDragging) {
      this._dimensionArr[this.state.placeHolderIndex].isPlaceHolder = false;

      //sort finished, callback fires
      if (isFunction(this.props.onSort)) {
        this.props.onSort(this.getSortData());
      }
    }

    if (this.isMounted() && _hasMouseMoved) {
      this.setState({
        isDragging: false,
        placeHolderIndex: null,
        left: null,
        top: null
      });
    }
  },

  /**
   * when children mounted, return its size(handled by SortableItemMixin)
   * @param  {object} offset {top:1, left:2}
   * @param  {number} width
   * @param  {number} height
   * @param  {number} fullWidth  (with margin)
   * @param  {number} fullHeight (with margin)
   * @param  {number} index
   */
  handleChildUpdate(offset, width, height, fullWidth, fullHeight, index) {
    assign(this._dimensionArr[index], {
      top: offset.top,
      left: offset.left,
      width: width,
      height: height,
      fullWidth: fullWidth,
      fullHeight: fullHeight
    });
  },

  /**
   * get new index of all items by cursor position
   * @param {object} offset {left: 12, top: 123}
   * @param {string} direction cursor moving direction, used to aviod blinking when
   *                 interchanging position of different width elements
   * @return {number}
   */
  getIndexByOffset(offset, direction) {
    if (!offset || !isNumeric(offset.top) || !isNumeric(offset.left)) {
      return 0;
    }

    const _dimensionArr = this._dimensionArr;
    const offsetX = offset.left;
    const offsetY = offset.top;
    const prevIndex = this.state.placeHolderIndex !== null ?
                   this.state.placeHolderIndex :
                   this._draggingIndex;
    let newIndex;

    _dimensionArr.every((item, index) => {
      const relativeLeft = offsetX - item.left;
      const relativeTop = offsetY - item.top;

      if (relativeLeft < item.fullWidth && relativeTop < item.fullHeight) {
        if (relativeLeft < item.fullWidth / 2 && direction === 'left') {
          newIndex = index;
        }
        else if(relativeLeft > item.fullWidth / 2 && direction === 'right'){
          newIndex = Math.min(index + 1, _dimensionArr.length - 1);
        }
        else if (relativeTop < item.fullHeight / 2 && direction === 'up') {
          newIndex = index;
        }
        else if (relativeTop > item.fullHeight / 2 && direction === 'down') {
          newIndex = index;
        }
        else {
          return true;
        }

        return false;
      }
      return true;
    });

    return newIndex !== undefined ? newIndex : prevIndex;
  },

  /**
   * untility function
   * @param  {array} arr
   * @param  {number} src
   * @param  {number} to
   * @return {array}
   */
  swapArrayItemPosition(arr, src, to) {
    if (!arr || !isNumeric(src) || !isNumeric(to)) {
      return arr;
    }

    const srcEl = arr.splice(src, 1)[0];
    arr.splice(to, 0, srcEl);
    return arr;
  },

  /**
   * calculate new offset
   * @param  {object} e MouseMove event
   * @return {object}   {left: 1, top: 1}
   */
  calculateNewOffset(e) {
    const deltaX = this._prevX - (e.pageX || e.clientX);
    const deltaY = this._prevY - (e.pageY || e.clientY);

    const prevLeft = this.state.left !== null ? this.state.left : this._initOffset.left;
    const prevTop = this.state.top !== null ? this.state.top : this._initOffset.top;
    const newLeft = prevLeft - deltaX;
    const newTop = prevTop - deltaY;

    return {
      left: newLeft,
      top: newTop
    };
  },

  /**
   * calculate new index and do swapping
   * @param  {object} e MouseMove event
   * @return {number}
   */
  calculateNewIndex(e) {
    let placeHolderIndex = this.state.placeHolderIndex !== null ?
                           this.state.placeHolderIndex :
                           this._draggingIndex;

    // Since `mousemove` is listened on document, when cursor move too fast,
    // `e.target` may be `body` or some other stuff instead of
    // `.ui-sortable-item`
    const target = get('.ui-sortable-dragging');
    const offset = position(target);

    const deltaX = Math.abs(this._prevX - (e.pageX || e.clientX));
    const deltaY = Math.abs(this._prevY - (e.pageY || e.clientY));

    let direction;
    // tend to move left/right
    if (deltaX > deltaY) {
      direction = this._prevX > (e.pageX || e.clientX) ? 'left' : 'right';
    }
    // tend to move up/down
    else {
      direction = this._prevY > (e.pageY || e.clientY) ? 'up' : 'down';
    }

    const newIndex = this.getIndexByOffset(offset, direction);
    if (newIndex !== placeHolderIndex) {
      this._dimensionArr = this.swapArrayItemPosition(this._dimensionArr, placeHolderIndex, newIndex);
      this._orderArr = this.swapArrayItemPosition(this._orderArr, placeHolderIndex, newIndex);
    }

    return newIndex;
  },

  getSortData() {
    return this._orderArr.map((itemIndex, index) => {
      const item = Array.isArray(this.props.children) ?
                   this.props.children[itemIndex] :
                   this.props.children;
      if (!item) {
        return undefined;
      }

      return item.props.sortData;
    });
  },

  /**
   * render all sortable children which mixined with SortableItemMixin
   */
  renderItems() {
    const {_dimensionArr, _orderArr} = this;
    let draggingItem;

    const items = _orderArr.map((itemIndex, index) => {
      let item = Array.isArray(this.props.children) ? this.props.children[itemIndex] : this.props.children;
      if (!item) {
        return undefined;
      }
      if (index === this._draggingIndex) {
        draggingItem = this.renderDraggingItem(item);
      }

      const isPlaceHolder = _dimensionArr[index].isPlaceHolder;
      const itemClassName = `ui-sortable-item ${isPlaceHolder && 'ui-sortable-placeholder'} ${this.state.isDragging && isPlaceHolder && 'visible'}`;

      return React.cloneElement(item, {
        key: index,
        sortableClassName: `${item.props.className} ${itemClassName}`,
        sortableIndex: index,
        onSortableItemReadyToMove: isPlaceHolder ? undefined : (e) => {
          this.handleMouseDown.call(this, e, index);
        },
        onSortableItemMount: this.handleChildUpdate
      });
    });

    return items.concat([draggingItem]);
  },

  /**
   * render the item that being dragged
   * @param  {object} item a reference of this.props.children
   */
  renderDraggingItem(item) {
    if (!item) {
      return;
    }

    const style = {
      top: this.state.top,
      left: this.state.left,
      width: this._dimensionArr[this._draggingIndex].width,
      height: this._dimensionArr[this._draggingIndex].height
    };
    return React.cloneElement(item, {
      sortableClassName: `${item.props.className} ui-sortable-item ui-sortable-dragging`,
      key: this._dimensionArr.length,
      sortableStyle: style,
      isDragging: true
    });
  },

  render() {
    const className = 'ui-sortable ' + (this.props.className || '');

    return (
      <div className={className}>
        {this.renderItems()}
      </div>
    );
  }
});

Sortable.SortableItemMixin = SortableItemMixin;

export default Sortable;
<commit_msg>feat: add `containment` props<commit_after>/**
 * @file react-anything-sortable
 * @author jasonslyvia
 */

/*eslint new-cap:0, consistent-return: 0 */

'use strict';

/**
 * @dependency
 */
import React, {PropTypes} from 'react';
import ReactDOM from 'react-dom';
import {on, off, isFunction, isNumeric, position, closest, get,
        assign} from './utils';
import SortableItemMixin from './SortableItemMixin';

/**
 * @class Sortable
 */
const Sortable = React.createClass({
  propTypes: {
    /**
     * callback fires after sort operation finish
     * function (dataSet){
     *   //dataSet sorted
     * }
     */
    onSort: PropTypes.func,
    className: PropTypes.string,
    containment: PropTypes.bool
  },

  getInitialState() {
    //keep tracking the dimension and coordinates of all children
    this._dimensionArr = this.props.children ?
                         ( Array.isArray(this.props.children) ? this.props.children.map(() => {return {}; }) : [{}] ) :
                         [];

    //keep tracking the order of all children
    this._orderArr = [];
    let i = 0;
    while(i < this._dimensionArr.length){
      this._orderArr.push(i++);
    }

    return {
      isDragging: false,
      placeHolderIndex: null,
      left: null,
      top: null
    };
  },

  componentDidUpdate() {
    const container = ReactDOM.findDOMNode(this);
    const rect = container.getBoundingClientRect();

    this._top = rect.top + document.body.scrollTop;
    this._left = rect.left + document.body.scrollLeft;
    this._bottom = this._top + rect.height;
    this._right = this._left + rect.width;
  },

  componentWillUnmount() {
    this.unbindEvent();
  },

  bindEvent() {
    //so that the focus won't be lost if cursor moving too fast
    this.__mouseMoveHandler = (e) => {
      /**
       * Since Chrome may trigger redundant mousemove event evne if
       * we didn't really move the mouse, we should make sure that
       * mouse coordinates really changed then respond to mousemove
       * event
       * @see https://code.google.com/p/chromium/issues/detail?id=327114
       */
      if (((e.pageX || e.clientX) === this._prevX && (e.pageY || e.clientY) === this._prevY) ||
          (this._prevX === null && this._prevY === null)) {
        return false;
      }

      this.handleMouseMove.call(this, e);
    };

    this.__mouseUpHandler = (e) => {
      this.handleMouseUp.call(this, e);
    };

    this.__touchMoveHandler = (e) => {
      this.handleMouseMove.call(this, {
        target: e.target,
        clientX: e.touches[0].clientX,
        clientY: e.touches[0].clientY,
        pageX: e.touches[0].pageX,
        pageY: e.touches[0].pageY
      });
    };

    this.__touchEndOrCancelHandler = (e) => {
      this.handleMouseUp.call(this, e);
    };

    if (!!('ontouchstart' in window)) {
      on(document, 'touchmove', this.__touchMoveHandler);
      on(document, 'touchend', this.__touchEndOrCancelHandler);
      on(document, 'touchcancel', this.__touchEndOrCancelHandler);
    }
    else {
      on(document, 'mousemove', this.__mouseMoveHandler);
      on(document, 'mouseup', this.__mouseUpHandler);
    }
  },

  unbindEvent() {
    if (!!('ontouchstart' in window)) {
      off(document, 'touchmove', this.__touchMoveHandler);
      off(document, 'touchend', this.__touchEndOrCancelHandler);
      off(document, 'touchcancel', this.__touchEndOrCancelHandler);
    }
    else {
      off(document, 'mousemove', this.__mouseMoveHandler);
      off(document, 'mouseup', this.__mouseUpHandler);
    }

    this.__mouseMoveHandler = null;
    this.__mouseUpHandler = null;
    this.__touchMoveHandler = null;
    this.__touchEndOrCancelHandler = null;
  },

  /**
   * getting ready for dragging
   * @param  {object} e     React event
   * @param  {numbner} index index of pre-dragging item
   */
  handleMouseDown(e, index) {
    this._draggingIndex = index;
    this._prevX = (e.pageX || e.clientX);
    this._prevY = (e.pageY || e.clientY);
    this._initOffset = e.offset;
    this._isReadyForDragging = true;
    this._hasInitDragging = false;

    //start listening mousemove and mouseup
    this.bindEvent();
  },

  /**
   * `add` a dragging item and re-calculating position of placeholder
   * @param  {object} e     React event
   */
  handleMouseMove(e) {
    this._isMouseMoving = true;

    if (!this._isReadyForDragging) {
      return false;
    }

    if (!this._hasInitDragging) {
      this._dimensionArr[this._draggingIndex].isPlaceHolder = true;
      this._hasInitDragging = false;
    }

    if (this.props.containment) {
      const x = e.pageX || e.clientX;
      const y = e.pageY || e.clientY;

      if (x < this._left || x > this._right || y < this._top || y > this._bottom) {
        return false;
      }
    }

    const newOffset = this.calculateNewOffset(e);
    const newIndex = this.calculateNewIndex(e);

    this._draggingIndex = newIndex;

    this.setState({
      isDragging: true,
      top: newOffset.top,
      left: newOffset.left,
      placeHolderIndex: newIndex
    });

    this._prevX = (e.pageX || e.clientX);
    this._prevY = (e.pageY || e.clientY);
  },

  /**
   * replace placeholder with dragging item
   * @param  {object} e     React event
   */
  handleMouseUp() {
    const _hasMouseMoved = this._isMouseMoving;
    this.unbindEvent();

    //reset temp lets
    this._draggingIndex = null;
    this._isReadyForDragging = false;
    this._isMouseMoving = false;
    this._initOffset = null;
    this._prevX = null;
    this._prevY = null;

    if (this.state.isDragging) {
      this._dimensionArr[this.state.placeHolderIndex].isPlaceHolder = false;

      //sort finished, callback fires
      if (isFunction(this.props.onSort)) {
        this.props.onSort(this.getSortData());
      }
    }

    if (this.isMounted() && _hasMouseMoved) {
      this.setState({
        isDragging: false,
        placeHolderIndex: null,
        left: null,
        top: null
      });
    }
  },

  /**
   * when children mounted, return its size(handled by SortableItemMixin)
   * @param  {object} offset {top:1, left:2}
   * @param  {number} width
   * @param  {number} height
   * @param  {number} fullWidth  (with margin)
   * @param  {number} fullHeight (with margin)
   * @param  {number} index
   */
  handleChildUpdate(offset, width, height, fullWidth, fullHeight, index) {
    assign(this._dimensionArr[index], {
      top: offset.top,
      left: offset.left,
      width: width,
      height: height,
      fullWidth: fullWidth,
      fullHeight: fullHeight
    });
  },

  /**
   * get new index of all items by cursor position
   * @param {object} offset {left: 12, top: 123}
   * @param {string} direction cursor moving direction, used to aviod blinking when
   *                 interchanging position of different width elements
   * @return {number}
   */
  getIndexByOffset(offset, direction) {
    if (!offset || !isNumeric(offset.top) || !isNumeric(offset.left)) {
      return 0;
    }

    const _dimensionArr = this._dimensionArr;
    const offsetX = offset.left;
    const offsetY = offset.top;
    const prevIndex = this.state.placeHolderIndex !== null ?
                   this.state.placeHolderIndex :
                   this._draggingIndex;
    let newIndex;

    _dimensionArr.every((item, index) => {
      const relativeLeft = offsetX - item.left;
      const relativeTop = offsetY - item.top;

      if (relativeLeft < item.fullWidth && relativeTop < item.fullHeight) {
        if (relativeLeft < item.fullWidth / 2 && direction === 'left') {
          newIndex = index;
        }
        else if(relativeLeft > item.fullWidth / 2 && direction === 'right'){
          newIndex = Math.min(index + 1, _dimensionArr.length - 1);
        }
        else if (relativeTop < item.fullHeight / 2 && direction === 'up') {
          newIndex = index;
        }
        else if (relativeTop > item.fullHeight / 2 && direction === 'down') {
          newIndex = index;
        }
        else {
          return true;
        }

        return false;
      }
      return true;
    });

    return newIndex !== undefined ? newIndex : prevIndex;
  },

  /**
   * untility function
   * @param  {array} arr
   * @param  {number} src
   * @param  {number} to
   * @return {array}
   */
  swapArrayItemPosition(arr, src, to) {
    if (!arr || !isNumeric(src) || !isNumeric(to)) {
      return arr;
    }

    const srcEl = arr.splice(src, 1)[0];
    arr.splice(to, 0, srcEl);
    return arr;
  },

  /**
   * calculate new offset
   * @param  {object} e MouseMove event
   * @return {object}   {left: 1, top: 1}
   */
  calculateNewOffset(e) {
    const deltaX = this._prevX - (e.pageX || e.clientX);
    const deltaY = this._prevY - (e.pageY || e.clientY);

    const prevLeft = this.state.left !== null ? this.state.left : this._initOffset.left;
    const prevTop = this.state.top !== null ? this.state.top : this._initOffset.top;
    const newLeft = prevLeft - deltaX;
    const newTop = prevTop - deltaY;

    return {
      left: newLeft,
      top: newTop
    };
  },

  /**
   * calculate new index and do swapping
   * @param  {object} e MouseMove event
   * @return {number}
   */
  calculateNewIndex(e) {
    let placeHolderIndex = this.state.placeHolderIndex !== null ?
                           this.state.placeHolderIndex :
                           this._draggingIndex;

    // Since `mousemove` is listened on document, when cursor move too fast,
    // `e.target` may be `body` or some other stuff instead of
    // `.ui-sortable-item`
    const target = get('.ui-sortable-dragging');
    const offset = position(target);

    const deltaX = Math.abs(this._prevX - (e.pageX || e.clientX));
    const deltaY = Math.abs(this._prevY - (e.pageY || e.clientY));

    let direction;
    // tend to move left/right
    if (deltaX > deltaY) {
      direction = this._prevX > (e.pageX || e.clientX) ? 'left' : 'right';
    }
    // tend to move up/down
    else {
      direction = this._prevY > (e.pageY || e.clientY) ? 'up' : 'down';
    }

    const newIndex = this.getIndexByOffset(offset, direction);
    if (newIndex !== placeHolderIndex) {
      this._dimensionArr = this.swapArrayItemPosition(this._dimensionArr, placeHolderIndex, newIndex);
      this._orderArr = this.swapArrayItemPosition(this._orderArr, placeHolderIndex, newIndex);
    }

    return newIndex;
  },

  getSortData() {
    return this._orderArr.map((itemIndex, index) => {
      const item = Array.isArray(this.props.children) ?
                   this.props.children[itemIndex] :
                   this.props.children;
      if (!item) {
        return undefined;
      }

      return item.props.sortData;
    });
  },

  /**
   * render all sortable children which mixined with SortableItemMixin
   */
  renderItems() {
    const {_dimensionArr, _orderArr} = this;
    let draggingItem;

    const items = _orderArr.map((itemIndex, index) => {
      let item = Array.isArray(this.props.children) ? this.props.children[itemIndex] : this.props.children;
      if (!item) {
        return undefined;
      }
      if (index === this._draggingIndex) {
        draggingItem = this.renderDraggingItem(item);
      }

      const isPlaceHolder = _dimensionArr[index].isPlaceHolder;
      const itemClassName = `ui-sortable-item ${isPlaceHolder && 'ui-sortable-placeholder'} ${this.state.isDragging && isPlaceHolder && 'visible'}`;

      return React.cloneElement(item, {
        key: index,
        sortableClassName: `${item.props.className} ${itemClassName}`,
        sortableIndex: index,
        onSortableItemReadyToMove: isPlaceHolder ? undefined : (e) => {
          this.handleMouseDown.call(this, e, index);
        },
        onSortableItemMount: this.handleChildUpdate
      });
    });

    return items.concat([draggingItem]);
  },

  /**
   * render the item that being dragged
   * @param  {object} item a reference of this.props.children
   */
  renderDraggingItem(item) {
    if (!item) {
      return;
    }

    const style = {
      top: this.state.top,
      left: this.state.left,
      width: this._dimensionArr[this._draggingIndex].width,
      height: this._dimensionArr[this._draggingIndex].height
    };
    return React.cloneElement(item, {
      sortableClassName: `${item.props.className} ui-sortable-item ui-sortable-dragging`,
      key: this._dimensionArr.length,
      sortableStyle: style,
      isDragging: true
    });
  },

  render() {
    const className = 'ui-sortable ' + (this.props.className || '');

    return (
      <div className={className}>
        {this.renderItems()}
      </div>
    );
  }
});

Sortable.SortableItemMixin = SortableItemMixin;

export default Sortable;
<|endoftext|>


<commit_before>'use strict';

import core from './core';
import array from './array/array';
import async from './async/async';
import dom from './dom/dom';
import globalEval from './eval/globalEval';
import Disposable from './disposable/Disposable';
import EventEmitter from './events/EventEmitter';
import EventEmitterProxy from './events/EventEmitterProxy';
import EventHandle from './events/EventHandle';
import EventHandler from './events/EventHandler';
import object from './object/object';
import string from './string/string';

export { core };
export { array };
export { async };
export { dom };
export { globalEval };
export { Disposable };
export { EventEmitter };
export { EventEmitterProxy };
export { EventHandle };
export { EventHandler };
export { object };
export { string };
<commit_msg>Leaves only existing modules in index.js<commit_after>'use strict';

import core from './core';
import array from './array/array';
import async from './async/async';
import Disposable from './disposable/Disposable';
import object from './object/object';
import string from './string/string';

export { core };
export { array };
export { async };
export { Disposable };
export { object };
export { string };
<|endoftext|>


<commit_before>import cron from 'node-cron';
import nodemailer from 'nodemailer';
import wreck from 'wreck';

const subreddits  = ['javascript', 'webdev', 'programming', 'node'];
const transporter = nodemailer.createTransport('smtps://user%40gmail.com:pass@smtp.gmail.com');

let articles = [];
let count = 0;
subreddits.forEach(function (sub) {

    const baseUrl     = 'https://www.reddit.com/r/';
    const query       = '/top.json?sort=top&t=week&limit=5';
    const url = baseUrl + sub + query;

    wreck.request('GET', url, {}, (error, response) => {

        wreck.read(response, null, (error, body) => {

            var data = JSON.parse(body.toString('utf8'));

            var mappedData = data.data.children.map((children) => {
                if (children.data.subreddit === 'puppies') {
                    return '' + '';
                } else {
                    return '' + children.data.title + '';
                }
            });

            var htmlData = mappedData.map((article) => {
                return '
'+ article +'
';
            });

            articles = Array.concat(articles, htmlData);

            count++;
            if (count === subreddits.length) {
                sendMail(articles);
            }
        });
    });
});

function sendMail(articles) {
    var mailOptions = {
        to: 'shaunwarman1@gmail.com',
        from: 'Mail Test',
        subject: 'Mail Test',
        html: articles.join(' ')
    };

    transporter.sendMail(mailOptions, (error, response) => {
        if (error){
            return console.log(error);
        }
        console.log('Message sent: ' + response.response);
    });
};
<commit_msg>Correctly use variables<commit_after>import cron from 'node-cron';
import nodemailer from 'nodemailer';
import wreck from 'wreck';

const subreddits  = ['javascript', 'webdev', 'programming', 'node'];

let articles = [];
let count = 0;
let transporter = nodemailer.createTransport('smtps://user%40gmail.com:pass@smtp.gmail.com');

subreddits.forEach(function (sub) {

    const baseUrl     = 'https://www.reddit.com/r/';
    const query       = '/top.json?sort=top&t=week&limit=5';
    const url = baseUrl + sub + query;

    wreck.request('GET', url, {}, (error, response) => {

        wreck.read(response, null, (error, body) => {

            let data = JSON.parse(body.toString('utf8'));

            var mappedData = data.data.children.map((children) => {
                if (children.data.subreddit === 'puppies') {
                    return '' + '';
                } else {
                    return '' + children.data.title + '';
                }
            });

            var htmlData = mappedData.map((article) => {
                return '
'+ article +'
';
            });

            articles = Array.concat(articles, htmlData);

            count++;
            if (count === subreddits.length) {
                sendMail(articles);
            }
        });
    });
});

function sendMail(articles) {
    let mailOptions = {
        to: 'shaunwarman1@gmail.com',
        from: 'Mail Test',
        subject: 'Mail Test',
        html: articles.join(' ')
    };

    transporter.sendMail(mailOptions, (error, response) => {
        if (error){
            return console.log(error);
        }
        console.log('Message sent: ' + response.response);
    });
};
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import Script from 'react-load-script'
import styled from 'styled-components'

const Wrapper = styled.div`
  flex: 1;
  display: flex;
`

const Editor = styled.div`
  flex: 1;
  display: flex;

  > iframe {
    flex: 1;
    width: 100%;
    height: 100%;
    min-height: ${props => props.minHeight || '500px'} !important;
    display: flex;
    border: 0px;
  }
`

export default class extends Component {
  render() {
    return (
      <Wrapper>
        <Script
          url="https://editor.unlayer.com/embed.js"
          onLoad={this.unlayerReady}
        />

        <Editor
          id="editor"
          style={this.props.style}
          minHeight={this.props.minHeight}
        />
      </Wrapper>
    )
  }

  unlayerReady = () => {
    const options = (this.props.options || {})

    if (this.props.projectId) {
      options.projectId = this.props.projectId
    }

    if (this.props.tools) {
      options.tools = this.props.tools
    }

    unlayer.init({
      ...options,
      id: 'editor',
      displayMode: 'email',
      locale: this.props.locale,
    })

    // All properties starting with on[Name] are registered as event listeners.
    for (const [key, value] of Object.entries(this.props)) {
      if (/^on/.test(key) && key != 'onLoad') {
        this.addEventListener(key, value)
      }
    }

    const { onLoad } = this.props
    onLoad && onLoad()
  }

  registerCallback = (type, callback) => {
    unlayer.registerCallback(type, callback)
  }

  addEventListener = (type, callback) => {
    unlayer.addEventListener(type, callback)
  }

  loadDesign = (design) => {
    unlayer.loadDesign(design)
  }

  saveDesign = (callback) => {
    unlayer.saveDesign(callback)
  }

  exportHtml = (callback) => {
    unlayer.exportHtml(callback)
  }

  setMergeTags = (mergeTags) => {
    unlayer.setMergeTags(mergeTags)
  }
}
<commit_msg>Use locale only if provided in props.<commit_after>import React, {Component} from 'react'
import Script from 'react-load-script'
import styled from 'styled-components'

const Wrapper = styled.div`
  flex: 1;
  display: flex;
`

const Editor = styled.div`
  flex: 1;
  display: flex;

  > iframe {
    flex: 1;
    width: 100%;
    height: 100%;
    min-height: ${props => props.minHeight || '500px'} !important;
    display: flex;
    border: 0px;
  }
`

export default class extends Component {
  render() {
    return (
      <Wrapper>
        <Script
          url="https://editor.unlayer.com/embed.js"
          onLoad={this.unlayerReady}
        />

        <Editor
          id="editor"
          style={this.props.style}
          minHeight={this.props.minHeight}
        />
      </Wrapper>
    )
  }

  unlayerReady = () => {
    const options = (this.props.options || {})

    if (this.props.projectId) {
      options.projectId = this.props.projectId
    }

    if (this.props.tools) {
      options.tools = this.props.tools
    }

    if (this.props.locale) {
      options.locale = this.props.locale
    }

    unlayer.init({
      ...options,
      id: 'editor',
      displayMode: 'email',
    })

    // All properties starting with on[Name] are registered as event listeners.
    for (const [key, value] of Object.entries(this.props)) {
      if (/^on/.test(key) && key != 'onLoad') {
        this.addEventListener(key, value)
      }
    }

    const { onLoad } = this.props
    onLoad && onLoad()
  }

  registerCallback = (type, callback) => {
    unlayer.registerCallback(type, callback)
  }

  addEventListener = (type, callback) => {
    unlayer.addEventListener(type, callback)
  }

  loadDesign = (design) => {
    unlayer.loadDesign(design)
  }

  saveDesign = (callback) => {
    unlayer.saveDesign(callback)
  }

  exportHtml = (callback) => {
    unlayer.exportHtml(callback)
  }

  setMergeTags = (mergeTags) => {
    unlayer.setMergeTags(mergeTags)
  }
}
<|endoftext|>