<commit_before>'use strict';

const getChannelURL = require('ember-source-channel-url');

module.exports = async function() {
  return {
    useYarn: true,
    scenarios: [
      {
        name: 'ember-lts-3.12',
        npm: {
          devDependencies: {
            'ember-source': '~3.12.0',
          },
        },
      },
      {
        name: 'ember-lts-3.16',
        npm: {
          devDependencies: {
            'ember-source': '~3.16.0',
          },
        },
      },
      {
        name: 'ember-release',
        npm: {
          devDependencies: {
            'ember-source': await getChannelURL('release'),
          },
        },
      },
      {
        name: 'ember-beta',
        npm: {
          devDependencies: {
            'ember-source': await getChannelURL('beta'),
          },
        },
      },
      {
        name: 'ember-canary',
        npm: {
          devDependencies: {
            'ember-source': await getChannelURL('canary'),
          },
        },
      },
      // The default `.travis.yml` runs this scenario via `npm test`,
      // not via `ember try`. It's still included here so that running
      // `ember try:each` manually or from a customized CI config will run it
      // along with all the other scenarios.
      {
        name: 'ember-default',
        npm: {
          devDependencies: {},
        },
      },
      {
        name: 'ember-default-with-jquery',
        env: {
          EMBER_OPTIONAL_FEATURES: JSON.stringify({
            'jquery-integration': true,
          }),
        },
        npm: {
          devDependencies: {
            '@ember/jquery': '^0.5.1',
          },
        },
      },
      {
        name: 'ember-classic',
        env: {
          EMBER_OPTIONAL_FEATURES: JSON.stringify({
            'application-template-wrapper': true,
            'default-async-observers': false,
            'template-only-glimmer-components': false,
          }),
        },
        npm: {
          ember: {
            edition: 'classic',
          },
        },
      },
    ],
  };
};
<commit_msg>fix(dev): Allow ember-canary to fail (#3104)<commit_after>'use strict';

const getChannelURL = require('ember-source-channel-url');

module.exports = async function() {
  return {
    useYarn: true,
    scenarios: [
      {
        name: 'ember-lts-3.12',
        npm: {
          devDependencies: {
            'ember-source': '~3.12.0',
          },
        },
      },
      {
        name: 'ember-lts-3.16',
        npm: {
          devDependencies: {
            'ember-source': '~3.16.0',
          },
        },
      },
      {
        name: 'ember-release',
        npm: {
          devDependencies: {
            'ember-source': await getChannelURL('release'),
          },
        },
      },
      {
        name: 'ember-beta',
        npm: {
          devDependencies: {
            'ember-source': await getChannelURL('beta'),
          },
        },
      },
      {
        name: 'ember-canary',
        allowedToFail: true,
        npm: {
          devDependencies: {
            'ember-source': await getChannelURL('canary'),
          },
        },
      },
      // The default `.travis.yml` runs this scenario via `npm test`,
      // not via `ember try`. It's still included here so that running
      // `ember try:each` manually or from a customized CI config will run it
      // along with all the other scenarios.
      {
        name: 'ember-default',
        npm: {
          devDependencies: {},
        },
      },
      {
        name: 'ember-default-with-jquery',
        env: {
          EMBER_OPTIONAL_FEATURES: JSON.stringify({
            'jquery-integration': true,
          }),
        },
        npm: {
          devDependencies: {
            '@ember/jquery': '^0.5.1',
          },
        },
      },
      {
        name: 'ember-classic',
        env: {
          EMBER_OPTIONAL_FEATURES: JSON.stringify({
            'application-template-wrapper': true,
            'default-async-observers': false,
            'template-only-glimmer-components': false,
          }),
        },
        npm: {
          ember: {
            edition: 'classic',
          },
        },
      },
    ],
  };
};
<|endoftext|>


<commit_before>define(function (require) {

  var React = require('react/addons'),
    Backbone = require('backbone'),
    stores = require('stores'),
    TagMultiSelect = require('components/common/tags/TagMultiSelect.react');

  return React.createClass({
    displayName: "Tags",

    propTypes: {
      onTagAdded: React.PropTypes.func.isRequired,
      onTagRemoved: React.PropTypes.func.isRequired,
      onTagCreated: React.PropTypes.func.isRequired,
      imageTags: React.PropTypes.instanceOf(Backbone.Collection).isRequired
    },

    getInitialState: function () {
      return {
        query: ""
      }
    },

    onQueryChange: function (query) {
      this.setState({query: query});
    },

    render: function () {
      var imageTags = this.props.imageTags,
        tags = stores.TagStore.getAll(),
        query = this.state.query;

      if (!tags) return
;

      if (query) {
        tags = tags.filter(function (tag) {
          return tag.get('name').toLowerCase().indexOf(query) >= 0;
        });
        tags = new Backbone.Collection(tags);
      }

      return (

          Image Tags

              Please include tags that will help users decide whether this image will suit their
              needs. You can include the operating system, installed software, or configuration information. E.g.
              Ubuntu,
              NGS Viewers, MAKER, QIIME, etc.

              For your convenience, we've automatically added the tags that were already on the instance.

            <TagMultiSelect
              models={tags}
              activeModels={imageTags}
              onModelAdded={this.props.onTagAdded}
              onModelRemoved={this.props.onTagRemoved}
              onModelCreated={this.props.onTagCreated}
              onQueryChange={this.onQueryChange}
              width={"100%"}
              placeholderText="Search by tag name..."
              />

      );
    }

  });

});
<commit_msg>fix tag creation functionality within image modal<commit_after>define(function (require) {

  var React = require('react/addons'),
    Backbone = require('backbone'),
    stores = require('stores'),
    actions = require('actions'),
		TagMultiSelect = require('components/common/tags/TagMultiSelect.react');

  return React.createClass({
    displayName: "Tags",

    propTypes: {
      onTagAdded: React.PropTypes.func.isRequired,
      onTagRemoved: React.PropTypes.func.isRequired,
      onTagCreated: React.PropTypes.func.isRequired,
      imageTags: React.PropTypes.instanceOf(Backbone.Collection).isRequired
    },

    getInitialState: function () {
      return {
        query: "",
				newTagName: "",
				newTagDescription: "",
				showTagCreateForm: false
      }
    },

		createTagAndAddToImage: function(){
			var newTag = actions.TagActions.create({name: this.state.newTagName, description: this.state.newTagDescription});
			this.props.onTagAdded(newTag);
			this.setState({newTagName: "", newTagDescription: "", showTagCreateForm: false});
		},

    onQueryChange: function (query) {
      this.setState({query: query});
    },

		onNewTagNameChange: function(name){
			this.setState({newTagName: name.target.value});
		},

		onNewTagDescriptionChange: function(description){
			this.setState({newTagDescription: description.target.value});
		},

		isSubmittable: function(){
			return this.state.newTagName && this.state.newTagDescription;
		},

		onCreateNewTag: function(tagName) {
			this.setState({
				newTagName: tagName,
				showTagCreateForm: true
			});
		},

    render: function () {
      var imageTags = this.props.imageTags,
        tags = stores.TagStore.getAll(),
        query = this.state.query,
				tagCreateForm = null;

			if(this.state.showTagCreateForm){
				tagCreateForm = (

						Create new tag
						<form>
							Name:

							<input className="form-control" type="text" onChange={this.onNewTagNameChange} value={this.state.newTagName} />

							Description:

							<textarea className="form-control" type="text" onChange={this.onNewTagDescriptionChange} value={this.state.newTagDescription} />

						</form>
						<button disabled={!this.isSubmittable()} onClick={this.createTagAndAddToImage}
							className="btn btn-primary btn-sm pull-right">
							Create and add
						</button>

				);
			}

      if (!tags) return
;

      if (query) {
        tags = tags.filter(function (tag) {
          return tag.get('name').toLowerCase().indexOf(query) >= 0;
        });
        tags = new Backbone.Collection(tags);
      }

      return (

          Image Tags

              Please include tags that will help users decide whether this image will suit their
              needs. You can include the operating system, installed software, or configuration information. E.g.
              Ubuntu,
              NGS Viewers, MAKER, QIIME, etc.

              For your convenience, we've automatically added the tags that were already on the instance.

            <TagMultiSelect
              models={tags}
              activeModels={imageTags}
              onCreateNewTag={this.onCreateNewTag}
							onModelAdded={this.props.onTagAdded}
              onModelRemoved={this.props.onTagRemoved}
              onModelCreated={this.props.onTagCreated}
              onQueryChange={this.onQueryChange}
              width={"100%"}
              placeholderText="Search by tag name..."
              />
						{tagCreateForm}

      );
    }

  });

});
<|endoftext|>


<commit_before>import querystring from 'querystring';
import _ from 'lodash';
import { format as formatDate, subDays } from 'date-fns';
import config from 'configs';
import { setPackages } from '../packages/packages.js';
import processPackagesStats from 'frontend/src/utils/processPackagesStats';
import getPackagesDownloads from 'utils/stats/getPackagesDownloads';
import isPackageName from 'utils/isPackageName';
import fetchReposCommitsStats from 'frontend/src/home/fetchReposCommitStats';
import withRender from './home.html';
import { downloadCsv } from './downloadCsv';

const palette = config.palette;
const presetPackages = _.shuffle(config.presetPackages);

const maxRequestPeriod = 365; // ~1 year

const {
  default: packageInput,
  emitter: packageEvents,
  packages,
} = require('../packages/packages');

const getModuleDataByNames = async (names, start, end) => {
  setTimeout(() => ga('send', 'pageview'));

  // set notify to false to prevent triggering route change
  setPackages(names, false);

  const operation = _.every(names, isPackageName)
    ? // names are npm packages
      getPackagesDownloadsOverPeriod(names, start, end).then(
        processPackagesStats,
      )
    : // names are github repo names
      fetchReposCommitsStats(names);

  return operation;
};

/**
 * Merge 2 statistic periods
 * @param period0 Period before period1
 * @param period1 Period after period0
 * @returns The merged period
 */
function mergePeriods(period0, period1) {
  const sumPackages = [];

  for (let p = 0; p < period0.length; ++p) {
    sumPackages.push({
      downloads: period0[p].downloads.concat(period1[p].downloads),
      package: period0[p].package,
      start: period0[p].start,
      end: period1[p].end,
    });
  }

  return sumPackages;
}

function maxDate(a, b) {
  return new Date(Math.max(a.getTime(), b.getTime()));
}

/**
 * @param names    {string} Package names
 * @param startDay {number} Start of period, 1 is yesterday
 * @param endDay   {number} End of period 0 is today
 * @returns {Promise<any>}
 */
function getPackagesDownloadsOverPeriod(names, startDay, endDay) {
  const requestPeriod = Math.min(maxRequestPeriod, startDay - endDay);
  const requestEndDay = startDay - requestPeriod;

  const startStats = new Date(Date.UTC(2015, 1, 10));

  const DATE_FORMAT = 'YYYY-MM-DD';
  const timezone = 'UTC';

  let startDate = maxDate(startStats, subDays(new Date(), startDay));
  let endDate = maxDate(startStats, subDays(new Date(), requestEndDay));

  startDate = formatDate(startDate, DATE_FORMAT, null, timezone);
  endDate = formatDate(endDate, DATE_FORMAT, null, timezone);

  const period1 = getPackagesDownloads(names, { startDate, endDate });

  if (requestEndDay > 0) {
    const period2 = getPackagesDownloadsOverPeriod(
      names,
      startDay - requestPeriod - 1,
      endDay,
    );
    return Promise.all([period1, period2]).then(res =>
      mergePeriods(res[0], res[1]),
    );
  }
  return period1;
}

export default withRender({
  created() {
    this.isLoading = true;
    getModuleDataByNames(
      this.moduleNames,
      this.$route.query.start ? this.$route.query.start : 365,
      this.$route.query.end ? this.$route.query.start : 0,
    ).then(moduleData => {
      this.isLoading = false;
      this.moduleData = moduleData;
    });
  },
  render: withRender.default,
  data() {
    return {
      presetPackages,
      samplePreset: [],
      moduleData: null,
      isLoading: true,
      palette,
      showWeekends: true,
      hoverCount: 0,
      twitterIcon: require('../assets/images/icon-twitter.svg'),
      shouldShowComments:
        window.innerWidth >= 1000 &&
        !JSON.parse(!!window.localStorage.getItem('shouldShowComments')),
    };
  },
  computed: {
    shareUrl() {
      return (
        this.moduleNames &&
        `http://npmcharts.com/compare/${this.moduleNames.join(',')}`
      );
    },
    twitterShareUrl() {
      return (
        this.shareUrl &&
        `https://twitter.com/intent/tweet?url=${window.encodeURIComponent(
          this.shareUrl,
        )}`
      );
    },
    isEmbedded() {
      return this.isMinimalMode;
    },
    queryString() {
      return querystring.stringify(this.$route.query);
    },
    interval() {
      return Number(
        this.$route.query.interval || this.$route.query.periodLength || 7,
      );
    },
    isUsingPresetPackages() {
      return !this.$route.params.packages;
    },
    moduleNames() {
      const moduleNames = this.isUsingPresetPackages
        ? _.sample(presetPackages)
        : this.$route.params.packages
            .split(',')
            .map(packageName => window.decodeURIComponent(packageName));
      return moduleNames;
    },
    isMinimalMode() {
      return this.$route.query.minimal === 'true';
    },
  },
  watch: {
    shouldShowComments() {
      this.$refs.graph.render();
    },
    isMinimalMode(isMinimalMode, prevIsMinimalMode) {
      console.log('watch isMinimalMode');
      if (isMinimalMode) {
        document.body.classList.add('minimal');
      } else {
        document.body.classList.remove('minimal');
      }
    },
  },
  mounted() {
    if (this.isMinimalMode) {
      document.body.classList.add('minimal');
    } else {
      document.body.classList.remove('minimal');
    }
    packageEvents.on('change', this.handlePackagesChange);
    // expose router so puppeteer can trigger route changes
    window.router = this.$router;
  },
  beforeDestroy() {
    packageEvents.removeListener('change', this.handlePackagesChange);
  },
  methods: {
    track(eventName, value) {
      ga('send', 'event', eventName, value);
    },
    getMergedQueryParams(params) {
      const merged = { ...this.$route.query, ...params };
      delete merged.periodLength;
      return merged;
    },
    handlePackagesChange() {
      const nextRouteSansQuery = `/compare/${packages.join(',')}`;
      if (this.$router.app.$route.path !== nextRouteSansQuery) {
        this.$router.push(`${nextRouteSansQuery}?${this.queryString}`);
      }
    },
    addPackage(packageName) {
      ga(
        'send',
        'event',
        'packageInput',
        'add',
        `${packageName} existing:${this.moduleNames}`,
      );

      this.$router.push({
        path: `/compare/${
          this.$route.params && this.$route.params.packages
            ? this.$route.params.packages + ',' + packageName
            : packageName
        }`,
        query: this.$route.query,
      });
    },
    clearPackages() {
      this.$router.push('/compare');
    },
    handleClickTwitter() {
      ga('send', 'event', 'share', 'twitter', this.twitterShareUrl);
      window.open(this.twitterShareUrl);
    },
    handleHoverTwitter() {
      this.hoverCount++;
      ga(
        'send',
        'event',
        'hoverShare',
        'twitter',
        this.twitterShareUrl,
        this.hoverCount,
      );
    },
    handleMouseEnterTwitter() {
      this.twitterEventTimeout = setTimeout(this.handleHoverTwitter, 500);
    },
    handleMouseLeaveTwitter() {
      clearTimeout(this.twitterEventTimeout);
    },
    handleDownloadCsv(e) {
      e.preventDefault();
      const moduleNames = this.moduleData.map(x => x.name);
      const moduleWithLongestHistory = _.maxBy(
        this.moduleData,
        x => x.downloads.length,
      );
      var csv = [['Date', ...moduleNames]]
        .concat(
          moduleWithLongestHistory.downloads.map(({ day, downloads }) => {
            return [day.toLocaleDateString()].concat(
              this.moduleData
                .map(
                  moduleData =>
                    moduleData.downloads.find(
                      x => x.day.toISOString() === day.toISOString(),
                    ).count || '',
                )
                .join(','),
            );
          }),
        )
        .join('\n');
      downloadCsv(csv, `${moduleNames}.csv`);
    },
    shuffle: _.shuffle,
  },
  components: {
    'package-input': packageInput,
    graph: require('../graph/graph').default,
  },
});
<commit_msg>add tracking for downloadcsv button<commit_after>import querystring from 'querystring';
import _ from 'lodash';
import { format as formatDate, subDays } from 'date-fns';
import config from 'configs';
import { setPackages } from '../packages/packages.js';
import processPackagesStats from 'frontend/src/utils/processPackagesStats';
import getPackagesDownloads from 'utils/stats/getPackagesDownloads';
import isPackageName from 'utils/isPackageName';
import fetchReposCommitsStats from 'frontend/src/home/fetchReposCommitStats';
import withRender from './home.html';
import { downloadCsv } from './downloadCsv';

const palette = config.palette;
const presetPackages = _.shuffle(config.presetPackages);

const maxRequestPeriod = 365; // ~1 year

const {
  default: packageInput,
  emitter: packageEvents,
  packages,
} = require('../packages/packages');

const getModuleDataByNames = async (names, start, end) => {
  setTimeout(() => ga('send', 'pageview'));

  // set notify to false to prevent triggering route change
  setPackages(names, false);

  const operation = _.every(names, isPackageName)
    ? // names are npm packages
      getPackagesDownloadsOverPeriod(names, start, end).then(
        processPackagesStats,
      )
    : // names are github repo names
      fetchReposCommitsStats(names);

  return operation;
};

/**
 * Merge 2 statistic periods
 * @param period0 Period before period1
 * @param period1 Period after period0
 * @returns The merged period
 */
function mergePeriods(period0, period1) {
  const sumPackages = [];

  for (let p = 0; p < period0.length; ++p) {
    sumPackages.push({
      downloads: period0[p].downloads.concat(period1[p].downloads),
      package: period0[p].package,
      start: period0[p].start,
      end: period1[p].end,
    });
  }

  return sumPackages;
}

function maxDate(a, b) {
  return new Date(Math.max(a.getTime(), b.getTime()));
}

/**
 * @param names    {string} Package names
 * @param startDay {number} Start of period, 1 is yesterday
 * @param endDay   {number} End of period 0 is today
 * @returns {Promise<any>}
 */
function getPackagesDownloadsOverPeriod(names, startDay, endDay) {
  const requestPeriod = Math.min(maxRequestPeriod, startDay - endDay);
  const requestEndDay = startDay - requestPeriod;

  const startStats = new Date(Date.UTC(2015, 1, 10));

  const DATE_FORMAT = 'YYYY-MM-DD';
  const timezone = 'UTC';

  let startDate = maxDate(startStats, subDays(new Date(), startDay));
  let endDate = maxDate(startStats, subDays(new Date(), requestEndDay));

  startDate = formatDate(startDate, DATE_FORMAT, null, timezone);
  endDate = formatDate(endDate, DATE_FORMAT, null, timezone);

  const period1 = getPackagesDownloads(names, { startDate, endDate });

  if (requestEndDay > 0) {
    const period2 = getPackagesDownloadsOverPeriod(
      names,
      startDay - requestPeriod - 1,
      endDay,
    );
    return Promise.all([period1, period2]).then(res =>
      mergePeriods(res[0], res[1]),
    );
  }
  return period1;
}

export default withRender({
  created() {
    this.isLoading = true;
    getModuleDataByNames(
      this.moduleNames,
      this.$route.query.start ? this.$route.query.start : 365,
      this.$route.query.end ? this.$route.query.start : 0,
    ).then(moduleData => {
      this.isLoading = false;
      this.moduleData = moduleData;
    });
  },
  render: withRender.default,
  data() {
    return {
      presetPackages,
      samplePreset: [],
      moduleData: null,
      isLoading: true,
      palette,
      showWeekends: true,
      hoverCount: 0,
      twitterIcon: require('../assets/images/icon-twitter.svg'),
      shouldShowComments:
        window.innerWidth >= 1000 &&
        !JSON.parse(!!window.localStorage.getItem('shouldShowComments')),
    };
  },
  computed: {
    shareUrl() {
      return (
        this.moduleNames &&
        `http://npmcharts.com/compare/${this.moduleNames.join(',')}`
      );
    },
    twitterShareUrl() {
      return (
        this.shareUrl &&
        `https://twitter.com/intent/tweet?url=${window.encodeURIComponent(
          this.shareUrl,
        )}`
      );
    },
    isEmbedded() {
      return this.isMinimalMode;
    },
    queryString() {
      return querystring.stringify(this.$route.query);
    },
    interval() {
      return Number(
        this.$route.query.interval || this.$route.query.periodLength || 7,
      );
    },
    isUsingPresetPackages() {
      return !this.$route.params.packages;
    },
    moduleNames() {
      const moduleNames = this.isUsingPresetPackages
        ? _.sample(presetPackages)
        : this.$route.params.packages
            .split(',')
            .map(packageName => window.decodeURIComponent(packageName));
      return moduleNames;
    },
    isMinimalMode() {
      return this.$route.query.minimal === 'true';
    },
  },
  watch: {
    shouldShowComments() {
      this.$refs.graph.render();
    },
    isMinimalMode(isMinimalMode, prevIsMinimalMode) {
      console.log('watch isMinimalMode');
      if (isMinimalMode) {
        document.body.classList.add('minimal');
      } else {
        document.body.classList.remove('minimal');
      }
    },
  },
  mounted() {
    if (this.isMinimalMode) {
      document.body.classList.add('minimal');
    } else {
      document.body.classList.remove('minimal');
    }
    packageEvents.on('change', this.handlePackagesChange);
    // expose router so puppeteer can trigger route changes
    window.router = this.$router;
  },
  beforeDestroy() {
    packageEvents.removeListener('change', this.handlePackagesChange);
  },
  methods: {
    track(eventName, value) {
      ga('send', 'event', eventName, value);
    },
    getMergedQueryParams(params) {
      const merged = { ...this.$route.query, ...params };
      delete merged.periodLength;
      return merged;
    },
    handlePackagesChange() {
      const nextRouteSansQuery = `/compare/${packages.join(',')}`;
      if (this.$router.app.$route.path !== nextRouteSansQuery) {
        this.$router.push(`${nextRouteSansQuery}?${this.queryString}`);
      }
    },
    addPackage(packageName) {
      ga(
        'send',
        'event',
        'packageInput',
        'add',
        `${packageName} existing:${this.moduleNames}`,
      );

      this.$router.push({
        path: `/compare/${
          this.$route.params && this.$route.params.packages
            ? this.$route.params.packages + ',' + packageName
            : packageName
        }`,
        query: this.$route.query,
      });
    },
    clearPackages() {
      this.$router.push('/compare');
    },
    handleClickTwitter() {
      ga('send', 'event', 'share', 'twitter', this.twitterShareUrl);
      window.open(this.twitterShareUrl);
    },
    handleHoverTwitter() {
      this.hoverCount++;
      ga(
        'send',
        'event',
        'hoverShare',
        'twitter',
        this.twitterShareUrl,
        this.hoverCount,
      );
    },
    handleMouseEnterTwitter() {
      this.twitterEventTimeout = setTimeout(this.handleHoverTwitter, 500);
    },
    handleMouseLeaveTwitter() {
      clearTimeout(this.twitterEventTimeout);
    },
    handleDownloadCsv(e) {
      e.preventDefault();
      const moduleNames = this.moduleData.map(x => x.name);
      const moduleWithLongestHistory = _.maxBy(
        this.moduleData,
        x => x.downloads.length,
      );
      var csv = [['Date', ...moduleNames]]
        .concat(
          moduleWithLongestHistory.downloads.map(({ day, downloads }) => {
            return [day.toLocaleDateString()].concat(
              this.moduleData
                .map(
                  moduleData =>
                    moduleData.downloads.find(
                      x => x.day.toISOString() === day.toISOString(),
                    ).count || '',
                )
                .join(','),
            );
          }),
        )
        .join('\n');
      this.track('download csv', moduleNames);
      downloadCsv(csv, `${moduleNames}.csv`);
    },
    shuffle: _.shuffle,
  },
  components: {
    'package-input': packageInput,
    graph: require('../graph/graph').default,
  },
});
<|endoftext|>


<commit_before>const temp = require('@cardstack/test-support/temp-helper');
const { makeRepo } = require('@cardstack/git/node-tests/support');
const ElasticAssert = require('@cardstack/elasticsearch/node-tests/assertions');
const JSONAPIFactory = require('@cardstack/test-support/jsonapi-factory');
const crypto = require('crypto');
const { wireItUp } = require('@cardstack/hub/main');

exports.createDefaultEnvironment = async function(initialModels = []) {
  let repoPath = await temp.mkdir('cardstack-server-test');

  // TODO: The git writer should make its own local repo when it
  // starts up the first time.
  let { head, repo } = await makeRepo(repoPath);

  let factory = new JSONAPIFactory();

  let user = factory.addResource('users', 'the-default-test-user').withAttributes({
    fullName: 'Default Test Environment',
    email: 'test@example.com'
  });

  factory.addResource('plugin-configs')
    .withAttributes({
      module: '@cardstack/hub',
    }).withRelated(
      'default-data-source',
      factory.addResource('data-sources')
        .withAttributes({
          'source-type': '@cardstack/git',
          params: { repo: repoPath }
        })
    );

  factory.addResource('plugin-configs')
    .withAttributes({ module: '@cardstack/git' });

  factory.addResource('grants')
    .withAttributes({
      mayCreateResource: true,
      mayUpdateResource: true,
      mayDeleteResource: true,
      mayWriteField: true
    }).withRelated('who', factory.addResource('groups', user.id));

  let container = await wireItUp(crypto.randomBytes(32), factory.getModels(), false);

  let writers = container.lookup('hub:writers');

  for (let model of inDependencyOrder(initialModels)) {
    // TODO: this one-by-one creation is still slower than is nice for
    // tests -- each time we write a schema model it invalidates the
    // schema cache, which needs to get rebuilt before we can write
    // the next one. Which also requires us to use inDependencyOrder,
    // which is kinda lame.
    //
    // On the other hand, this is a high-quality test of our ability
    // to build up the entire state in the same way an end user would
    // via JSONAPI requests. If we optimize this away, we should also
    // add some tests like that that are similarly comprehensive.
    let response = await writers.create('master', user.data, model.type, model);
    head = response.meta.version;
  }

  await container.lookup('hub:indexers').update({ realTime: true });

  Object.assign(container, {
    user: user.data,
    head,
    repo,
    repoPath,
    setUserId(id) {
      let m = this.lookup(`middleware:${require.resolve('@cardstack/hub/node-tests/test-authenticator/middleware')}`);
      m.userId = id;
    }
  });
  return container;
};

exports.destroyDefaultEnvironment = async function(env) {
  await env.teardown();
  await destroyIndices();
  await temp.cleanup();
};

async function destroyIndices() {
  let ea = new ElasticAssert();
  await ea.deleteContentIndices();
}

function inDependencyOrder(models) {
  let priority = ['default-values', 'plugin-configs', 'constraints', 'fields', 'content-types'];
  return priority.map(type => models.filter(m => m.type === type)).reduce((a,b) => a.concat(b), []).concat(models.filter(m => !priority.includes(m.type)));
}
<commit_msg>this was superceded by code in @cardstack/test-support/env<commit_after><|endoftext|>


<commit_before>import { BodyComponent, suffixCssClasses } from 'mjml-core'
import { flow, identity, join, filter } from 'lodash/fp'

const makeBackgroundString = flow(filter(identity), join(' '))
export default class MjSection extends BodyComponent {
  static allowedAttributes = {
    'background-color': 'color',
    'background-url': 'string',
    'background-repeat': 'enum(repeat,no-repeat)',
    'background-size': 'string',
    'background-position-x': 'enum(left,center,right)',
    'background-position-y': 'enum(top,center,bottom)',
    border: 'string',
    'border-bottom': 'string',
    'border-left': 'string',
    'border-radius': 'string',
    'border-right': 'string',
    'border-top': 'string',
    direction: 'enum(ltr,rtl)',
    'full-width': 'enum(full-width)',
    padding: 'unit(px,%){1,4}',
    'padding-top': 'unit(px,%)',
    'padding-bottom': 'unit(px,%)',
    'padding-left': 'unit(px,%)',
    'padding-right': 'unit(px,%)',
    'text-align': 'enum(left,center,right)',
    'text-padding': 'unit(px,%){1,4}',
  }

  static defaultAttributes = {
    'background-repeat': 'repeat',
    'background-size': 'auto',
    'background-position-x': 'center',
    'background-position-y': 'top',
    direction: 'ltr',
    padding: '20px 0',
    'text-align': 'center',
    'text-padding': '4px 4px 4px 0',
  }

  getChildContext() {
    const { box } = this.getBoxWidths()

    return {
      ...this.context,
      containerWidth: `${box}px`,
    }
  }

  getStyles() {
    const { containerWidth } = this.context

    const fullWidth = this.isFullWidth()

    const background = this.getAttribute('background-url')
      ? {
        background: this.getBackground(),
        'background-position': `${this.getAttribute('background-position-x')} ${this.getAttribute('background-position-y')}`,
        // background size and position has to be seperate since yahoo does not support shorthand background css property
        'background-size': this.getAttribute('background-size'),
      }
      : {
          background: this.getAttribute('background-color'),
          'background-color': this.getAttribute('background-color'),
        }

    return {
      tableFullwidth: {
        ...(fullWidth ? background : {}),
        width: '100%',
        'border-radius': this.getAttribute('border-radius'),
      },
      table: {
        ...(fullWidth ? {} : background),
        width: '100%',
        'border-radius': this.getAttribute('border-radius'),
      },
      td: {
        border: this.getAttribute('border'),
        'border-bottom': this.getAttribute('border-bottom'),
        'border-left': this.getAttribute('border-left'),
        'border-right': this.getAttribute('border-right'),
        'border-top': this.getAttribute('border-top'),
        direction: this.getAttribute('direction'),
        'font-size': '0px',
        padding: this.getAttribute('padding'),
        'padding-bottom': this.getAttribute('padding-bottom'),
        'padding-left': this.getAttribute('padding-left'),
        'padding-right': this.getAttribute('padding-right'),
        'padding-top': this.getAttribute('padding-top'),
        'text-align': this.getAttribute('text-align'),
      },
      div: {
        ...(fullWidth ? {} : background),
        margin: '0px auto',
        'border-radius': this.getAttribute('border-radius'),
        'max-width': containerWidth,
      },
      innerDiv: {
        'line-height': '0',
        'font-size': '0',
      },
    }
  }

  getBackground() {
    return makeBackgroundString([
      this.getAttribute('background-color'),
      ...(this.hasBackground()
        ? [
            `url(${this.getAttribute('background-url')})`,
            this.getAttribute('background-position-x'),
            this.getAttribute('background-position-y'),
            `/ ${this.getAttribute('background-size')}`,
            this.getAttribute('background-repeat'),
          ]
        : []),
    ])
  }

  hasBackground() {
    return this.getAttribute('background-url') != null
  }

  isFullWidth() {
    return this.getAttribute('full-width') === 'full-width'
  }

  renderBefore() {
    const { containerWidth } = this.context

    return `

    `
  }

  // eslint-disable-next-line class-methods-use-this
  renderAfter() {
    return `

    `
  }

  renderWrappedChildren() {
    const { children } = this.props

    return `

      ${this.renderChildren(children, {
        renderer: component =>
          component.constructor.isRawElement()
            ? component.render()
            : `

            ${component.render()}

    `,
      })}

    `
  }

  renderWithBackground(content) {
    const fullWidth = this.isFullWidth()

    const { containerWidth } = this.context

    let vOriginPosX, vOriginPosY
    let vSizeAttributes = {}

    // this logic is different when using repeat or no-repeat
    if (this.getAttribute('background-repeat') == "repeat") {
      switch (this.getAttribute('background-position-x')) {
        case 'left': vOriginPosX = '0'; break;
        case 'center': vOriginPosX = '0.5'; break;
        case 'right': vOriginPosX = '1'; break;
        default: vOriginPosX = '0.5'; break;
      }
      switch (this.getAttribute('background-position-y')) {
        case 'top': vOriginPosY = '0'; break;
        case 'center': vOriginPosY = '0.5'; break;
        case 'bottom': vOriginPosY = '1'; break;
        default: vOriginPosY = '0'; break;
      }
    } else {

    }

    // If background size is either cover or contain, we tell VML to keep the aspect
    // and fill the entire element.
    if (this.getAttribute('background-size') === 'cover' ||
      this.getAttribute('background-size') === 'contain') {
      vSizeAttributes = {
        size: '1,1',
        aspect: this.getAttribute('background-size') == 'cover' ? 'atleast' : 'atmost',
      }
    }

    return `

          ${content}

    `
  }

  renderSection() {
    const hasBackground = this.hasBackground()

    return `

        ${hasBackground
          ? `<div ${this.htmlAttributes({ style: 'innerDiv' })}>`
          : ''}

                  ${this.renderWrappedChildren()}

        ${hasBackground ? '
' : ''}

    `
  }

  renderFullWidth() {
    const content = this.hasBackground()
      ? this.renderWithBackground(`
        ${this.renderBefore()}
        ${this.renderSection()}
        ${this.renderAfter()}
      `)
      : `
        ${this.renderBefore()}
        ${this.renderSection()}
        ${this.renderAfter()}
      `

    return `

              ${content}

    `
  }

  renderSimple() {
    const section = this.renderSection()

    return `
      ${this.renderBefore()}
      ${this.hasBackground() ? this.renderWithBackground(section) : section}
      ${this.renderAfter()}
    `
  }

  render() {
    return this.isFullWidth() ? this.renderFullWidth() : this.renderSimple()
  }
}
<commit_msg>added no-repeat functionality for outlook.<commit_after>import { BodyComponent, suffixCssClasses } from 'mjml-core'
import { flow, identity, join, filter } from 'lodash/fp'

const makeBackgroundString = flow(filter(identity), join(' '))
export default class MjSection extends BodyComponent {
  static allowedAttributes = {
    'background-color': 'color',
    'background-url': 'string',
    'background-repeat': 'enum(repeat,no-repeat)',
    'background-size': 'string',
    'background-position-x': 'enum(left,center,right)',
    'background-position-y': 'enum(top,center,bottom)',
    border: 'string',
    'border-bottom': 'string',
    'border-left': 'string',
    'border-radius': 'string',
    'border-right': 'string',
    'border-top': 'string',
    direction: 'enum(ltr,rtl)',
    'full-width': 'enum(full-width)',
    padding: 'unit(px,%){1,4}',
    'padding-top': 'unit(px,%)',
    'padding-bottom': 'unit(px,%)',
    'padding-left': 'unit(px,%)',
    'padding-right': 'unit(px,%)',
    'text-align': 'enum(left,center,right)',
    'text-padding': 'unit(px,%){1,4}',
  }

  static defaultAttributes = {
    'background-repeat': 'repeat',
    'background-size': 'auto',
    'background-position-x': 'center',
    'background-position-y': 'top',
    direction: 'ltr',
    padding: '20px 0',
    'text-align': 'center',
    'text-padding': '4px 4px 4px 0',
  }

  getChildContext() {
    const { box } = this.getBoxWidths()

    return {
      ...this.context,
      containerWidth: `${box}px`,
    }
  }

  getStyles() {
    const { containerWidth } = this.context

    const fullWidth = this.isFullWidth()

    const background = this.getAttribute('background-url')
      ? {
        background: this.getBackground(),
        'background-position': `${this.getAttribute('background-position-x')} ${this.getAttribute('background-position-y')}`,
        // background size and position has to be seperate since yahoo does not support shorthand background css property
        'background-size': this.getAttribute('background-size'),
      }
      : {
          background: this.getAttribute('background-color'),
          'background-color': this.getAttribute('background-color'),
        }

    return {
      tableFullwidth: {
        ...(fullWidth ? background : {}),
        width: '100%',
        'border-radius': this.getAttribute('border-radius'),
      },
      table: {
        ...(fullWidth ? {} : background),
        width: '100%',
        'border-radius': this.getAttribute('border-radius'),
      },
      td: {
        border: this.getAttribute('border'),
        'border-bottom': this.getAttribute('border-bottom'),
        'border-left': this.getAttribute('border-left'),
        'border-right': this.getAttribute('border-right'),
        'border-top': this.getAttribute('border-top'),
        direction: this.getAttribute('direction'),
        'font-size': '0px',
        padding: this.getAttribute('padding'),
        'padding-bottom': this.getAttribute('padding-bottom'),
        'padding-left': this.getAttribute('padding-left'),
        'padding-right': this.getAttribute('padding-right'),
        'padding-top': this.getAttribute('padding-top'),
        'text-align': this.getAttribute('text-align'),
      },
      div: {
        ...(fullWidth ? {} : background),
        margin: '0px auto',
        'border-radius': this.getAttribute('border-radius'),
        'max-width': containerWidth,
      },
      innerDiv: {
        'line-height': '0',
        'font-size': '0',
      },
    }
  }

  getBackground() {
    return makeBackgroundString([
      this.getAttribute('background-color'),
      ...(this.hasBackground()
        ? [
            `url(${this.getAttribute('background-url')})`,
            this.getAttribute('background-position-x'),
            this.getAttribute('background-position-y'),
            `/ ${this.getAttribute('background-size')}`,
            this.getAttribute('background-repeat'),
          ]
        : []),
    ])
  }

  hasBackground() {
    return this.getAttribute('background-url') != null
  }

  isFullWidth() {
    return this.getAttribute('full-width') === 'full-width'
  }

  renderBefore() {
    const { containerWidth } = this.context

    return `

    `
  }

  // eslint-disable-next-line class-methods-use-this
  renderAfter() {
    return `

    `
  }

  renderWrappedChildren() {
    const { children } = this.props

    return `

      ${this.renderChildren(children, {
        renderer: component =>
          component.constructor.isRawElement()
            ? component.render()
            : `

            ${component.render()}

    `,
      })}

    `
  }

  renderWithBackground(content) {
    const fullWidth = this.isFullWidth()

    const { containerWidth } = this.context

    let vOriginPosX, vOriginPosY
    let vSizeAttributes = {}

    // this logic is different when using repeat or no-repeat
    if (this.getAttribute('background-repeat') == "repeat") {
      switch (this.getAttribute('background-position-x')) {
        case 'left': vOriginPosX = '0'; break;
        case 'center': vOriginPosX = '0.5'; break;
        case 'right': vOriginPosX = '1'; break;
        default: vOriginPosX = '0.5'; break;
      }
      switch (this.getAttribute('background-position-y')) {
        case 'top': vOriginPosY = '0'; break;
        case 'center': vOriginPosY = '0.5'; break;
        case 'bottom': vOriginPosY = '1'; break;
        default: vOriginPosY = '0'; break;
      }
    } else {
      switch (this.getAttribute('background-position-x')) {
        case 'left': vOriginPosX = '-0.5'; break;
        case 'center': vOriginPosX = '0'; break;
        case 'right': vOriginPosX = '0.5'; break;
        default: vOriginPosX = '0'; break;
      }
      switch (this.getAttribute('background-position-y')) {
        case 'top': vOriginPosY = '-0.5'; break;
        case 'center': vOriginPosY = '0'; break;
        case 'bottom': vOriginPosY = '0.5'; break;
        default: vOriginPosY = '-1'; break;
      }
    }

    // If background size is either cover or contain, we tell VML to keep the aspect
    // and fill the entire element.
    if (this.getAttribute('background-size') === 'cover' ||
      this.getAttribute('background-size') === 'contain') {
      vSizeAttributes = {
        size: '1,1',
        aspect: this.getAttribute('background-size') == 'cover' ? 'atleast' : 'atmost',
      }
    }

    return `

          ${content}

    `
  }

  renderSection() {
    const hasBackground = this.hasBackground()

    return `

        ${hasBackground
          ? `<div ${this.htmlAttributes({ style: 'innerDiv' })}>`
          : ''}

                  ${this.renderWrappedChildren()}

        ${hasBackground ? '
' : ''}

    `
  }

  renderFullWidth() {
    const content = this.hasBackground()
      ? this.renderWithBackground(`
        ${this.renderBefore()}
        ${this.renderSection()}
        ${this.renderAfter()}
      `)
      : `
        ${this.renderBefore()}
        ${this.renderSection()}
        ${this.renderAfter()}
      `

    return `

              ${content}

    `
  }

  renderSimple() {
    const section = this.renderSection()

    return `
      ${this.renderBefore()}
      ${this.hasBackground() ? this.renderWithBackground(section) : section}
      ${this.renderAfter()}
    `
  }

  render() {
    return this.isFullWidth() ? this.renderFullWidth() : this.renderSimple()
  }
}
<|endoftext|>


<commit_before>/* eslint-disable no-empty */
import path from 'path';
import fse from 'fs-extra';
import get from 'lodash/get';
import set from 'lodash/set';
import has from 'lodash/has';
import Ajv from 'ajv';
import ajvKeywords from 'ajv-keywords';
import identity from 'lodash/identity';
import { Module } from 'oors';
import { graphqlExpress, graphiqlExpress } from 'apollo-server-express';
import { execute, subscribe } from 'graphql';
import { PubSub } from 'graphql-subscriptions';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import merge from 'lodash/merge';
import { makeExecutableSchema, makeRemoteExecutableSchema, mergeSchemas } from 'graphql-tools';
import { express as voyagerMiddleware } from 'graphql-voyager/middleware';
import playgroundMiddleware from 'graphql-playground-middleware-express';
import mainResolvers from './graphql/resolvers';
import modulesResolvers from './graphql/modulesResolvers';
import LoadersMap from './libs/LoadersMap';
import * as decorators from './decorators';

class Gql extends Module {
  static schema = {
    type: 'object',
    properties: {
      graphiql: {
        type: 'object',
        properties: {
          enabled: {
            type: 'boolean',
            default: true,
          },
          params: {
            type: 'object',
            properties: {
              endpointURL: {
                type: 'string',
                default: '/graphql',
              },
              subscriptionsEndpoint: {
                type: 'string',
              },
            },
            default: {},
          },
        },
        default: {},
      },
      voyager: {
        type: 'object',
        properties: {
          enabled: {
            type: 'boolean',
            default: true,
          },
          params: {
            type: 'object',
            properties: {
              endpointURL: {
                type: 'string',
                default: '/graphql',
              },
            },
            default: {},
          },
        },
        default: {},
      },
      playground: {
        type: 'object',
        properties: {
          enabled: {
            type: 'boolean',
            default: true,
          },
          params: {
            type: 'object',
            properties: {
              endpointURL: {
                type: 'string',
                default: '/graphql',
              },
            },
            default: {},
          },
        },
        default: {},
      },
      middlewarePivot: {
        type: 'string',
        default: 'isMethod',
      },
      configureSchema: {
        instanceof: 'Function',
      },
      exposeModules: {
        type: 'boolean',
        default: true,
      },
      subscriptions: {
        type: 'object',
        properties: {
          enabled: {
            type: 'boolean',
            default: true,
          },
          path: {
            type: 'string',
            default: '/subscriptions',
          },
          createPubSub: {
            instanceof: 'Function',
          },
          serverOptions: {
            type: 'object',
            default: {},
          },
        },
        default: {},
      },
      getServerOptions: {
        instanceof: 'Function',
      },
    },
  };

  name = 'oors.graphQL';
  typeDefs = [];
  resolvers = mainResolvers;
  middlewares = []; // for resolvers
  gqlContext = {
    ajv: new Ajv({
      allErrors: true,
      verbose: true,
      async: 'es7',
      useDefaults: true,
    }),
  };
  pubsub = undefined;
  loaders = new LoadersMap();

  initialize() {
    ajvKeywords(this.gqlContext.ajv, 'instanceof');
  }

  async setup({ exposeModules, subscriptions }, manager) {
    const createPubSub = this.getConfig('subscriptions.createPubSub', () => new PubSub());

    if (subscriptions.enabled) {
      this.pubsub = await createPubSub(manager);
    }

    const { logger } = await this.dependency('oors.logger');
    const {
      loaders,
      collectFromModule,
      addTypeDefs,
      addTypeDefsByPath,
      addResolvers,
      addMiddleware,
      addLoader,
      addLoaders,
    } = this;

    await this.runHook('load', collectFromModule, {
      pubsub: this.pubsub,
      addTypeDefs,
      addTypeDefsByPath,
      addResolvers,
      addMiddleware,
      addLoader,
    });

    if (exposeModules) {
      this.addResolvers(modulesResolvers);
      await this.addTypeDefsByPath(path.resolve(__dirname, './graphql/modulesTypeDefs.graphql'));
    }

    await this.runHook('buildContext', () => {}, {
      context: this.gqlContext,
    });

    const schema = this.buildSchema({ logger });

    const schemas = (await this.runHook('getSchema', () => {}, {
      schema,
      mergeSchemas,
      makeExecutableSchema,
      makeRemoteExecutableSchema,
    })).filter(s => s);

    const finalSchema = schemas.length
      ? mergeSchemas({
          schemas: [schema, ...schemas],
        })
      : schema;

    this.setupSubscriptionServer(finalSchema);

    this.loadMiddlewares(finalSchema);

    this.export({
      schema: finalSchema,
      loaders,
      addMiddleware,
      addLoader,
      addLoaders,
    });
  }

  addTypeDefs = typeDefs => {
    this.typeDefs.push(typeDefs);
  };

  addResolvers = resolvers => {
    merge(this.resolvers, resolvers);
  };

  addMiddleware = (matcher, middleware) => {
    this.middlewares.push({
      matcher: typeof matcher === 'string' ? new RegExp(`^${matcher}$`) : matcher,
      middleware,
    });

    return this;
  };

  addLoader = (...args) => {
    this.loaders.add(...args);
  };

  addLoaders = (...args) => {
    this.loaders.multiAdd(...args);
  };

  addTypeDefsByPath = async filePath => {
    this.addTypeDefs(await fse.readFile(filePath, 'utf8'));
  };

  async loadFromDir(dirPath) {
    try {
      const stat = await fse.stat(dirPath);
      const isDirectory = stat && stat.isDirectory();
      if (!isDirectory) {
        return;
      }
    } catch (err) {
      return;
    }

    try {
      await this.addTypeDefsByPath(path.join(dirPath, 'typeDefs.graphql'));
    } catch (err) {}

    try {
      // eslint-disable-next-line import/no-dynamic-require, global-require
      const resolvers = require(`${dirPath}/resolvers`);
      if (resolvers.default) {
        Object.assign(resolvers, resolvers.default);
        delete resolvers.default;
      }
      this.addResolvers(resolvers);
    } catch (err) {}
  }

  collectFromModule = async module => {
    if (!module.getConfig('graphql.autoload', true)) {
      return;
    }

    try {
      if (has(module, 'graphql')) {
        this.addTypeDefs(get(module, 'graphql.typeDefs', ''));
        this.addResolvers(get(module, 'graphql.resolvers', {}));
        if (has(module, 'graphql.typeDefsPath')) {
          await this.addTypeDefsByPath(get(module, 'graphql.typeDefsPath'));
        }
      } else {
        await this.loadFromDir(path.resolve(path.dirname(module.filePath), 'graphql'));
      }
    } catch (err) {}
  };

  buildSchema({ logger }) {
    const resolvers = this.resolvers;

    this.applyMiddlewares(resolvers);

    return makeExecutableSchema(
      this.getConfig('configureSchema', identity)({
        typeDefs: this.typeDefs,
        resolvers,
        logger: {
          log: err => logger.error(err),
        },
        allowUndefinedInResolve: false,
      }),
    );
  }

  applyMiddlewares(resolvers) {
    Object.keys(resolvers).forEach(type => {
      Object.keys(resolvers[type]).forEach(field => {
        const branch = `${type}.${field}`;
        const middlewares = this.middlewares.filter(({ matcher }) => matcher.test(branch));
        if (!middlewares.length) {
          return;
        }

        const resolver = [...middlewares]
          .reverse()
          .reduce(
            (acc, { middleware }) => (...args) => middleware(...args, acc),
            get(resolvers, branch),
          );

        set(resolvers, branch, resolver);
      });
    });
  }

  setupSubscriptionServer(schema) {
    if (!this.getConfig('subscriptions.enabled')) {
      return;
    }

    const { pubsub } = this;
    const { server } = this.app;
    const subscriptionServer = new SubscriptionServer(
      {
        execute,
        subscribe,
        schema,
        onConnect: connectionParams => ({ ...connectionParams }),
        onOperation: (message, params) => ({ ...params, context: this.gqlContext }),
        ...this.getConfig('subscriptions.serverOptions'),
      },
      {
        server,
        path: this.getConfig('subscriptions.path'),
      },
    );

    this.gqlContext.pubsub = pubsub;

    this.export({
      pubsub,
      subscriptionServer,
    });
  }

  loadMiddlewares(schema) {
    this.app.middlewares.insertBefore(
      this.getConfig('middlewarePivot'),
      this.getGraphQLMiddleware(schema),
      this.getGraphiQLMiddleware(),
      this.getVoyagerMiddleware(),
      this.getPlaygroundMiddleware(),
    );
  }

  getGraphQLMiddleware(schema) {
    return {
      id: 'graphql',
      path: '/graphql',
      factory: graphqlExpress,
      params: req => ({
        schema,
        context: {
          ...this.gqlContext,
          req,
          app: req.app,
          user: req.user,
          loaders: this.loaders.build(),
        },
        formatError: err => {
          if (err.originalError && err.originalError.code === 'VALIDATION_ERROR') {
            Object.assign(err, {
              errors: err.originalError.errors,
            });
          }

          return err;
        },
        tracing: true,
        cacheControl: true,
        ...this.getConfig('getServerOptions', () => ({}))(req),
      }),
    };
  }

  getGraphiQLMiddleware() {
    return {
      id: 'graphiql',
      path: '/graphiql',
      factory: graphiqlExpress,
      params: this.getConfig('graphiql.params'),
      enabled: this.getConfig('graphiql.enabled'),
    };
  }

  getVoyagerMiddleware() {
    return {
      id: 'voyager',
      path: '/voyager',
      factory: ({ endpointURL }) => voyagerMiddleware({ endpointUrl: endpointURL }),
      params: this.getConfig('voyager.params'),
      enabled: this.getConfig('voyager.enabled'),
    };
  }

  getPlaygroundMiddleware() {
    return {
      id: 'playground',
      path: '/playground',
      factory: ({ endpointURL }) => playgroundMiddleware({ endpoint: endpointURL }),
      params: this.getConfig('playground.params'),
      enabled: this.getConfig('playground.enabled'),
    };
  }
}

export { Gql as default, decorators };
export { default as Resolvers } from './libs/Resolvers';
<commit_msg>building a proper context for subscriptions<commit_after>/* eslint-disable no-empty */
import path from 'path';
import fse from 'fs-extra';
import get from 'lodash/get';
import set from 'lodash/set';
import has from 'lodash/has';
import Ajv from 'ajv';
import ajvKeywords from 'ajv-keywords';
import identity from 'lodash/identity';
import { Module } from 'oors';
import { graphqlExpress, graphiqlExpress } from 'apollo-server-express';
import { execute, subscribe } from 'graphql';
import { PubSub } from 'graphql-subscriptions';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import merge from 'lodash/merge';
import { makeExecutableSchema, makeRemoteExecutableSchema, mergeSchemas } from 'graphql-tools';
import { express as voyagerMiddleware } from 'graphql-voyager/middleware';
import playgroundMiddleware from 'graphql-playground-middleware-express';
import mainResolvers from './graphql/resolvers';
import modulesResolvers from './graphql/modulesResolvers';
import LoadersMap from './libs/LoadersMap';
import * as decorators from './decorators';

class Gql extends Module {
  static schema = {
    type: 'object',
    properties: {
      graphiql: {
        type: 'object',
        properties: {
          enabled: {
            type: 'boolean',
            default: true,
          },
          params: {
            type: 'object',
            properties: {
              endpointURL: {
                type: 'string',
                default: '/graphql',
              },
              subscriptionsEndpoint: {
                type: 'string',
              },
            },
            default: {},
          },
        },
        default: {},
      },
      voyager: {
        type: 'object',
        properties: {
          enabled: {
            type: 'boolean',
            default: true,
          },
          params: {
            type: 'object',
            properties: {
              endpointURL: {
                type: 'string',
                default: '/graphql',
              },
            },
            default: {},
          },
        },
        default: {},
      },
      playground: {
        type: 'object',
        properties: {
          enabled: {
            type: 'boolean',
            default: true,
          },
          params: {
            type: 'object',
            properties: {
              endpointURL: {
                type: 'string',
                default: '/graphql',
              },
            },
            default: {},
          },
        },
        default: {},
      },
      middlewarePivot: {
        type: 'string',
        default: 'isMethod',
      },
      configureSchema: {
        instanceof: 'Function',
      },
      exposeModules: {
        type: 'boolean',
        default: true,
      },
      subscriptions: {
        type: 'object',
        properties: {
          enabled: {
            type: 'boolean',
            default: true,
          },
          path: {
            type: 'string',
            default: '/subscriptions',
          },
          createPubSub: {
            instanceof: 'Function',
          },
          serverOptions: {
            type: 'object',
            default: {},
          },
        },
        default: {},
      },
      getServerOptions: {
        instanceof: 'Function',
      },
    },
  };

  name = 'oors.graphQL';
  typeDefs = [];
  resolvers = mainResolvers;
  middlewares = []; // for resolvers
  gqlContext = {
    ajv: new Ajv({
      allErrors: true,
      verbose: true,
      async: 'es7',
      useDefaults: true,
    }),
  };
  pubsub = undefined;
  loaders = new LoadersMap();

  initialize() {
    ajvKeywords(this.gqlContext.ajv, 'instanceof');
  }

  async setup({ exposeModules, subscriptions }, manager) {
    const createPubSub = this.getConfig('subscriptions.createPubSub', () => new PubSub());

    if (subscriptions.enabled) {
      this.pubsub = await createPubSub(manager);
    }

    const { logger } = await this.dependency('oors.logger');
    const {
      loaders,
      collectFromModule,
      addTypeDefs,
      addTypeDefsByPath,
      addResolvers,
      addMiddleware,
      addLoader,
      addLoaders,
    } = this;

    await this.runHook('load', collectFromModule, {
      pubsub: this.pubsub,
      addTypeDefs,
      addTypeDefsByPath,
      addResolvers,
      addMiddleware,
      addLoader,
    });

    if (exposeModules) {
      this.addResolvers(modulesResolvers);
      await this.addTypeDefsByPath(path.resolve(__dirname, './graphql/modulesTypeDefs.graphql'));
    }

    await this.runHook('buildContext', () => {}, {
      context: this.gqlContext,
    });

    const schema = this.buildSchema({ logger });

    const schemas = (await this.runHook('getSchema', () => {}, {
      schema,
      mergeSchemas,
      makeExecutableSchema,
      makeRemoteExecutableSchema,
    })).filter(s => s);

    const finalSchema = schemas.length
      ? mergeSchemas({
          schemas: [schema, ...schemas],
        })
      : schema;

    this.setupSubscriptionServer(finalSchema);

    this.loadMiddlewares(finalSchema);

    this.export({
      schema: finalSchema,
      loaders,
      addMiddleware,
      addLoader,
      addLoaders,
    });
  }

  addTypeDefs = typeDefs => {
    this.typeDefs.push(typeDefs);
  };

  addResolvers = resolvers => {
    merge(this.resolvers, resolvers);
  };

  addMiddleware = (matcher, middleware) => {
    this.middlewares.push({
      matcher: typeof matcher === 'string' ? new RegExp(`^${matcher}$`) : matcher,
      middleware,
    });

    return this;
  };

  addLoader = (...args) => {
    this.loaders.add(...args);
  };

  addLoaders = (...args) => {
    this.loaders.multiAdd(...args);
  };

  addTypeDefsByPath = async filePath => {
    this.addTypeDefs(await fse.readFile(filePath, 'utf8'));
  };

  async loadFromDir(dirPath) {
    try {
      const stat = await fse.stat(dirPath);
      const isDirectory = stat && stat.isDirectory();
      if (!isDirectory) {
        return;
      }
    } catch (err) {
      return;
    }

    try {
      await this.addTypeDefsByPath(path.join(dirPath, 'typeDefs.graphql'));
    } catch (err) {}

    try {
      // eslint-disable-next-line import/no-dynamic-require, global-require
      const resolvers = require(`${dirPath}/resolvers`);
      if (resolvers.default) {
        Object.assign(resolvers, resolvers.default);
        delete resolvers.default;
      }
      this.addResolvers(resolvers);
    } catch (err) {}
  }

  collectFromModule = async module => {
    if (!module.getConfig('graphql.autoload', true)) {
      return;
    }

    try {
      if (has(module, 'graphql')) {
        this.addTypeDefs(get(module, 'graphql.typeDefs', ''));
        this.addResolvers(get(module, 'graphql.resolvers', {}));
        if (has(module, 'graphql.typeDefsPath')) {
          await this.addTypeDefsByPath(get(module, 'graphql.typeDefsPath'));
        }
      } else {
        await this.loadFromDir(path.resolve(path.dirname(module.filePath), 'graphql'));
      }
    } catch (err) {}
  };

  buildSchema({ logger }) {
    const resolvers = this.resolvers;

    this.applyMiddlewares(resolvers);

    return makeExecutableSchema(
      this.getConfig('configureSchema', identity)({
        typeDefs: this.typeDefs,
        resolvers,
        logger: {
          log: err => logger.error(err),
        },
        allowUndefinedInResolve: false,
      }),
    );
  }

  applyMiddlewares(resolvers) {
    Object.keys(resolvers).forEach(type => {
      Object.keys(resolvers[type]).forEach(field => {
        const branch = `${type}.${field}`;
        const middlewares = this.middlewares.filter(({ matcher }) => matcher.test(branch));
        if (!middlewares.length) {
          return;
        }

        const resolver = [...middlewares]
          .reverse()
          .reduce(
            (acc, { middleware }) => (...args) => middleware(...args, acc),
            get(resolvers, branch),
          );

        set(resolvers, branch, resolver);
      });
    });
  }

  setupSubscriptionServer(schema) {
    if (!this.getConfig('subscriptions.enabled')) {
      return;
    }

    const { pubsub } = this;
    const { server } = this.app;
    const subscriptionServer = new SubscriptionServer(
      {
        execute,
        subscribe,
        schema,
        onConnect: connectionParams => ({ ...connectionParams }),
        onOperation: (message, params) => ({
          ...params,
          context: {
            ...this.gqlContext,
            ...(params.context || {}),
          },
        }),
        ...this.getConfig('subscriptions.serverOptions'),
      },
      {
        server,
        path: this.getConfig('subscriptions.path'),
      },
    );

    this.gqlContext.pubsub = pubsub;

    this.export({
      pubsub,
      subscriptionServer,
    });
  }

  loadMiddlewares(schema) {
    this.app.middlewares.insertBefore(
      this.getConfig('middlewarePivot'),
      this.getGraphQLMiddleware(schema),
      this.getGraphiQLMiddleware(),
      this.getVoyagerMiddleware(),
      this.getPlaygroundMiddleware(),
    );
  }

  getGraphQLMiddleware(schema) {
    return {
      id: 'graphql',
      path: '/graphql',
      factory: graphqlExpress,
      params: req => ({
        schema,
        context: {
          ...this.gqlContext,
          req,
          app: req.app,
          user: req.user,
          loaders: this.loaders.build(),
        },
        formatError: err => {
          if (err.originalError && err.originalError.code === 'VALIDATION_ERROR') {
            Object.assign(err, {
              errors: err.originalError.errors,
            });
          }

          return err;
        },
        tracing: true,
        cacheControl: true,
        ...this.getConfig('getServerOptions', () => ({}))(req),
      }),
    };
  }

  getGraphiQLMiddleware() {
    return {
      id: 'graphiql',
      path: '/graphiql',
      factory: graphiqlExpress,
      params: this.getConfig('graphiql.params'),
      enabled: this.getConfig('graphiql.enabled'),
    };
  }

  getVoyagerMiddleware() {
    return {
      id: 'voyager',
      path: '/voyager',
      factory: ({ endpointURL }) => voyagerMiddleware({ endpointUrl: endpointURL }),
      params: this.getConfig('voyager.params'),
      enabled: this.getConfig('voyager.enabled'),
    };
  }

  getPlaygroundMiddleware() {
    return {
      id: 'playground',
      path: '/playground',
      factory: ({ endpointURL }) => playgroundMiddleware({ endpoint: endpointURL }),
      params: this.getConfig('playground.params'),
      enabled: this.getConfig('playground.enabled'),
    };
  }
}

export { Gql as default, decorators };
export { default as Resolvers } from './libs/Resolvers';
<|endoftext|>


<commit_before>angular.module('mainCtrl', ['ui.bootstrap'])

.controller('mainController', function($rootScope, $location, $window, Auth, User, War) {
	var vm = this;

	// These are assigned here mainly to save clutter in the HTML page
	vm.authSizing = 'col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 col-lg-4 col-lg-offset-4';
	vm.mainPageSizing = 'col-xs-12 col-sm-8 col-sm-offset-2 col-lg-6 col-lg-offset-3';
	vm.buttonRoutingSizing = 'col-xs-8 col-xs-offset-2 col-sm-6 col-sm-offset-3 col-lg-4 col-lg-offset-4';

	// get info if a person is logged in
	vm.loggedIn = Auth.isLoggedIn();

	// Keeps the proper Navbar option highlighted; reflects the page currently visiting
	var setActive = function(active) {
		document.getElementById('navUsers').className = '';
		document.getElementById('navWars').className = '';
		document.getElementById('navHome').className = '';
		document.getElementById('navProfile').className = '';
		document.getElementById(active).className = 'active';
	}

	var checkRoutePermission = function(route) {

		/* Routes that anyone can go to */
		if (route == '/') {  // home
			setActive('navHome');
			return;
		}
		if (route == '/login') {
			if (vm.loggedIn){  // If a user is already logged in, don't let them go to the login page via "back"
				setActive('navHome');
				$location.path('/');
			} else {  // User is not logged in, let them go there
				setActive('navProfile');
			}
			return;
		}

		/* Logged In Authentication */
		if (!vm.loggedIn){
			setActive('navHome');
			$location.path('/');
			return;
		}

		/* Routes that only people who are logged in can go to */
		if (route == '/wars') {
			setActive('navWars');
			return;
		}
		if (route.indexOf('/wars/view') > -1) {
			setActive('navWars');
			return;
		}
		if (route.indexOf('/users/profile') > -1) {
			setActive('navProfile');
			return;
		}

		/* Admin In Authentication */
		if (!vm.userInfo.admin){
			setActive('navHome');
			$location.path('/');
			return;
		}

		if (route.indexOf('/wars/edit') > -1) {
			setActive('navWars');
			return;
		}
		if (route.indexOf('/users') > -1) {
			setActive('navUsers');
			return;
		}

	}

	// check to see if a user is logged in on every request
	var routeChange = function() {

		vm.loggedIn = Auth.isLoggedIn();

		// get user information on route change
		if (vm.loggedIn) {
			Auth.getUser().then(function(data) {
				vm.userInfo = data.data;
				checkRoutePermission($location.path());
			});
		} else {
			checkRoutePermission($location.path());
		}

		// Grab all users & wars if routing to main page
		if ($location.path() == '/') {
			User.partial().then(function(data) {
				if (data.data.success) {

					// bind the users that come back to vm.users
					vm.users = data.data.data;
					vm.users.sort(function(a, b) {
						return (a.dateJoined < b.dateJoined) ? -1 : (a.dateJoined > b.dateJoined) ? 1 : 0;
					});

					for (var i = 0; i < vm.users.length; i++) {
						vm.users[i].dateJoined = new Date(Number(vm.users[i].dateJoined)); // Convert milliseconds to date object
					};

					vm.leaders = [];
					vm.elders  = [];
					vm.members = [];

					for (var i = 0; i < vm.users.length; i++) {
						if (vm.users[i].title.indexOf('Leader') > -1)
							vm.leaders.push(vm.users[i]);
						else if (vm.users[i].title == 'Elder')
							vm.elders.push(vm.users[i]);
						else
							vm.members.push(vm.users[i]);
					};

					delete vm.users;

				} else {
					// Do Something
				}
			});
			War.partial().then(function(data) {
				if (data.data.success) {

					// Bind the wars that come back to vm.wars
					vm.wars = data.data.data;

					vm.wars.sort(function(a, b) {
						return (a.start < b.start) ? -1 : (a.start > b.start) ? 1 : 0;
					});

					now = new Date();
					for (var i = 0; i < vm.wars.length; i++) {
						vm.wars[i].start = new Date(Number(vm.wars[i].start - (now.getTimezoneOffset() * 60000))); // Convert milliseconds to date object
					};

				} else {
					// Do Something
				}
			});
		}
	};

	$rootScope.$on('$routeChangeStart', function () {
		routeChange();
	});

	vm.viewProfile = function(name) {
		console.log("HERE");
		if (vm.loggedIn)
			$location.path('/users/profile/' + name);
	}

	vm.currentWar = function(start) {
		$location.path('/wars/view/' + Number(start).toString());
	}

	// function to handle login form
	vm.processAuth = function() {
		vm.processing = true;

		login = function() {
			Auth.login(vm.loginData.name, vm.loginData.password)
				.then(function(data) {
					vm.processing = false;
					// if a user successfully logs in, redirect to users page
					if (data.data.success) {
						$location.path('/');
					}
					else {
						vm.loginData.password = '';  // Clear password
						vm.loginData.passwordConfirm = '';  // Clear password
						vm.error = data.data.message;
					}
			});
		}

		// clear the error
		vm.error = '';
		vm.message = '';
		if (!vm.isRegistering) {  // Then user is trying to log in
			login();
		} else if (vm.loginData.id && vm.loginData.passwordConfirm) {
			if (vm.loginData.password != vm.loginData.passwordConfirm){
				vm.error = 'Passwords do not match';
				vm.processing = false;
			} else {  // Passwords match
				// use the create function in the userService
				User.create(vm.loginData)
					.then(function(data) {
						vm.processing = false;
						if (data.data.success) {
							vm.message = 'Registration Complete: Waiting on Admin to Approve';
							vm.loginData.password = '';  // Clear password
							vm.loginData.passwordConfirm = '';  // Clear password
							vm.processing = false;
						} else {
							vm.loginData.password = '';  // Clear password
							vm.loginData.passwordConfirm = '';  // Clear password
							vm.error = data.data.message;
							vm.processing = false;
						}
				});
			}
		} else {  // The user has left out some piece of information
			vm.error = 'Please fill in all fields';
			vm.processing = false;
		}
	};

	// function to handle logging out
	vm.doLogout = function() {
		Auth.logout();
		vm.loggedIn = false;
		// reset all user info
		vm.userInfo = {};
		$location.path('/login');
	};

	var forceSSL = function () {
		if ($location.protocol() !== 'https') {
			$window.location.href = $location.absUrl().replace('http', 'https');
		}
	};
	forceSSL();

});<commit_msg>Checking something..<commit_after>angular.module('mainCtrl', ['ui.bootstrap'])

.controller('mainController', function($rootScope, $location, $window, Auth, User, War) {
	var vm = this;

	// These are assigned here mainly to save clutter in the HTML page
	vm.authSizing = 'col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 col-lg-4 col-lg-offset-4';
	vm.mainPageSizing = 'col-xs-12 col-sm-8 col-sm-offset-2 col-lg-6 col-lg-offset-3';
	vm.buttonRoutingSizing = 'col-xs-8 col-xs-offset-2 col-sm-6 col-sm-offset-3 col-lg-4 col-lg-offset-4';

	// get info if a person is logged in
	vm.loggedIn = Auth.isLoggedIn();

	// Keeps the proper Navbar option highlighted; reflects the page currently visiting
	var setActive = function(active) {
		document.getElementById('navUsers').className = '';
		document.getElementById('navWars').className = '';
		document.getElementById('navHome').className = '';
		document.getElementById('navProfile').className = '';
		document.getElementById(active).className = 'active';
	}

	var checkRoutePermission = function(route) {

		/* Routes that anyone can go to */
		if (route == '/') {  // home
			setActive('navHome');
			return;
		}
		if (route == '/login') {
			if (vm.loggedIn){  // If a user is already logged in, don't let them go to the login page via "back"
				setActive('navHome');
				$location.path('/');
			} else {  // User is not logged in, let them go there
				setActive('navProfile');
			}
			return;
		}

		/* Logged In Authentication */
		if (!vm.loggedIn){
			setActive('navHome');
			$location.path('/');
			return;
		}

		/* Routes that only people who are logged in can go to */
		if (route == '/wars') {
			setActive('navWars');
			return;
		}
		if (route.indexOf('/wars/view') > -1) {
			setActive('navWars');
			return;
		}
		if (route.indexOf('/users/profile') > -1) {
			setActive('navProfile');
			return;
		}

		/* Admin In Authentication */
		if (!vm.userInfo.admin){
			setActive('navHome');
			$location.path('/');
			return;
		}

		if (route.indexOf('/wars/edit') > -1) {
			setActive('navWars');
			return;
		}
		if (route.indexOf('/users') > -1) {
			setActive('navUsers');
			return;
		}

	}

	// check to see if a user is logged in on every request
	var routeChange = function() {

		vm.loggedIn = Auth.isLoggedIn();

		// get user information on route change
		if (vm.loggedIn) {
			Auth.getUser().then(function(data) {
				vm.userInfo = data.data;
				checkRoutePermission($location.path());
			});
		} else {
			checkRoutePermission($location.path());
		}

		// Grab all users & wars if routing to main page
		if ($location.path() == '/') {
			User.partial().then(function(data) {
				if (data.data.success) {

					// bind the users that come back to vm.users
					vm.users = data.data.data;
					vm.users.sort(function(a, b) {
						return (a.dateJoined < b.dateJoined) ? -1 : (a.dateJoined > b.dateJoined) ? 1 : 0;
					});

					for (var i = 0; i < vm.users.length; i++) {
						vm.users[i].dateJoined = new Date(Number(vm.users[i].dateJoined)); // Convert milliseconds to date object
					};

					vm.leaders = [];
					vm.elders  = [];
					vm.members = [];

					for (var i = 0; i < vm.users.length; i++) {
						if (vm.users[i].title.indexOf('Leader') > -1)
							vm.leaders.push(vm.users[i]);
						else if (vm.users[i].title == 'Elder')
							vm.elders.push(vm.users[i]);
						else
							vm.members.push(vm.users[i]);
					};

					delete vm.users;

				} else {
					// Do Something
				}
			});
			War.partial().then(function(data) {
				if (data.data.success) {

					// Bind the wars that come back to vm.wars
					vm.wars = data.data.data;

					vm.wars.sort(function(a, b) {
						return (a.start < b.start) ? -1 : (a.start > b.start) ? 1 : 0;
					});

					now = new Date();
					for (var i = 0; i < vm.wars.length; i++) {
						vm.wars[i].start = new Date(Number(vm.wars[i].start - (now.getTimezoneOffset() * 60000))); // Convert milliseconds to date object
					};

				} else {
					// Do Something
				}
			});
		}
	};

	$rootScope.$on('$routeChangeStart', function () {
		routeChange();
	});

	vm.viewProfile = function(name) {
		console.log("HERE");
		if (vm.loggedIn)
			$location.path('/users/profile/' + name);
	}

	vm.currentWar = function(start) {
		$location.path('/wars/view/' + Number(start).toString());
	}

	// function to handle login form
	vm.processAuth = function() {
		vm.processing = true;

		login = function() {
			Auth.login(vm.loginData.name, vm.loginData.password)
				.then(function(data) {
					vm.processing = false;
					// if a user successfully logs in, redirect to users page
					if (data.data.success) {
						$location.path('/');
					}
					else {
						vm.loginData.password = '';  // Clear password
						vm.loginData.passwordConfirm = '';  // Clear password
						vm.error = data.data.message;
					}
			});
		}

		// clear the error
		vm.error = '';
		vm.message = '';
		if (!vm.isRegistering) {  // Then user is trying to log in
			login();
		} else if (vm.loginData.id && vm.loginData.passwordConfirm) {
			if (vm.loginData.password != vm.loginData.passwordConfirm){
				vm.error = 'Passwords do not match';
				vm.processing = false;
			} else {  // Passwords match
				// use the create function in the userService
				User.create(vm.loginData)
					.then(function(data) {
						vm.processing = false;
						if (data.data.success) {
							vm.message = 'Registration Complete: Waiting on Admin to Approve';
							vm.loginData.password = '';  // Clear password
							vm.loginData.passwordConfirm = '';  // Clear password
							vm.processing = false;
						} else {
							vm.loginData.password = '';  // Clear password
							vm.loginData.passwordConfirm = '';  // Clear password
							vm.error = data.data.message;
							vm.processing = false;
						}
				});
			}
		} else {  // The user has left out some piece of information
			vm.error = 'Please fill in all fields';
			vm.processing = false;
		}
	};

	// function to handle logging out
	vm.doLogout = function() {
		Auth.logout();
		vm.loggedIn = false;
		// reset all user info
		vm.userInfo = {};
		$location.path('/login');
	};

	// var forceSSL = function () {
	// 	if ($location.protocol() !== 'https') {
	// 		$window.location.href = $location.absUrl().replace('http', 'https');
	// 	}
	// };
	// forceSSL();

});<|endoftext|>


<commit_before>$(function ()
{
	// select active link in menu
	(function ()
	{
		var currentPage = window.location.href;
		currentPage = currentPage.replace(window.location.search, '');
		currentPage = currentPage.replace(/\/create$/, '');
		currentPage = currentPage.replace(/\/([0-9]+)\/edit/, '');

		var currentPageLink = $('#side-menu a[href="' + currentPage + '"]');
		currentPageLink.addClass('active').parents('li').addClass('active').end().parents('ul').addClass('collapse').addClass('in');
	})();

	// create tooltips
	(function ()
	{
		$('html').tooltip({
			selector: "[data-toggle=tooltip]",
			container: "body"
		})
	})();

	// autofocus first text input
	(function ()
	{
		$('input[type="text"]:first').focus();
	})();

});<commit_msg>Menu item highlight fix<commit_after>$(function ()
{
	// select active link in menu
	(function ()
	{
		var currentPage = window.location.href;
		currentPage = currentPage.replace(window.location.search, '');
		currentPage = currentPage.replace(/\/create$/, '');
		currentPage = currentPage.replace(/\/([0-9]+)\/edit/, '');

		var currentPageLink = $('#side-menu a[href="' + currentPage + '"]');
		if ( ! currentPageLink.length)
		{
			currentPageLink = $('#side-menu a[href^="' + currentPage + '?"]');
		}
		currentPageLink.addClass('active').parents('li').addClass('active').end().parents('ul').addClass('collapse').addClass('in');
	})();

	// create tooltips
	(function ()
	{
		$('html').tooltip({
			selector: "[data-toggle=tooltip]",
			container: "body"
		})
	})();

	// autofocus first text input
	(function ()
	{
		$('input[type="text"]:first').focus();
	})();

});<|endoftext|>