<commit_before>// Aurelia
import { inject, LogManager } from 'aurelia-framework';

// Third party
import { json } from 'd3';

// Injectables
import States from 'services/states';

// Utils etc.
import $ from 'utils/dom-el';
import debounce from 'utils/debounce';
import ping from 'utils/ping';
import { createHgComponent as hg } from 'hglib';
import { requestNextAnimationFrame } from 'utils/request-animation-frame';
import {
  setGrayscale,
  setFragmentsHighlight,
  setInteractions,
  setSelectionView
} from 'components/higlass/higlass-actions';
import {
  GRAYSCALE_COLORS
} from 'components/higlass/higlass-defaults';

import arraysEqual from 'utils/arrays-equal';
import deepClone from 'utils/deep-clone';

const logger = LogManager.getLogger('higlass');

const OPTIONS = {
  bounded: true
};

@inject(States)
export class Higlass {
  constructor (states) {
    // Link the Redux store
    this.store = states.store;
    this.store.subscribe(this.update.bind(this));

    this.renderDb = debounce(this.render.bind(this), 50);
    this.checkColumnsDb = debounce(this.checkColumns.bind(this), 150);

    this.isLoading = true;

    this.locationTracker = {};
  }

  attached () {
    this.update();

    setTimeout(() => { this.isLoading = false; }, 150);
  }

  /* ----------------------- Getter / Setter Variables ---------------------- */

  get isErrored () {
    return this._isErrored;
  }

  set isErrored (value) {
    if (!value) {
      this.errorMsg = undefined;
    }

    this._isErrored = value;
  }

  /* ---------------------------- Custom Methods ---------------------------- */

  areServersAvailable (config) {
    let servers;

    try {
      // This needs refactoring but we need to wait until Higlass's config has
      // been refactored.
      servers = [config.views[0].tracks.center[0].contents[0].server];
    } catch (e) {
      return Promise.reject(Error('Broken config'));
    }

    return Promise.all(servers.map(server => ping(server)));
  }

  checkColumns (newColumns) {
    if (this.columns !== newColumns) {
      this.columns = newColumns;

      // For HiGlass to rerender
      requestNextAnimationFrame(() => {
        $(window).dispatch('resize', 'HTMLEvents');
      });
    }
  }

  /**
   * Extract loci in modified BEDPE format for higlass.
   *
   * @param {object} fgmConfig - Fragment config.
   * @return {array} List of loci.
   */
  extractLoci (fgmConfig) {
    const dataIdxChrom1 = fgmConfig.fragmentsHeader.indexOf('chrom1');
    const dataIdxStart1 = fgmConfig.fragmentsHeader.indexOf('start1');
    const dataIdxEnd1 = fgmConfig.fragmentsHeader.indexOf('end1');
    const dataIdxChrom2 = fgmConfig.fragmentsHeader.indexOf('chrom2');
    const dataIdxStart2 = fgmConfig.fragmentsHeader.indexOf('start2');
    const dataIdxEnd2 = fgmConfig.fragmentsHeader.indexOf('end2');

    return fgmConfig.fragments.map(fragment => [
      `chr${fragment[dataIdxChrom1]}`,
      fragment[dataIdxStart1],
      fragment[dataIdxEnd1],
      `chr${fragment[dataIdxChrom2]}`,
      fragment[dataIdxStart2],
      fragment[dataIdxEnd2],
      'rgba(255, 85, 0, 0.8)'
    ]);
  }

  /**
   * Handles changes of highlighting fragments.
   */
  fragmentsHighlightChangeHandler () {
    this.store.dispatch(setFragmentsHighlight(!this.fragmentsHighlight));

    return true;
  }

  /**
   * Handles changes of grayscale.
   */
  grayscaleChangeHandler () {
    this.store.dispatch(setGrayscale(!this.grayscale));

    return true;
  }

  /**
   * Helper method for showing an error.
   *
   * @param {string} errorMsg - Error message to be displayed.
   */
  hasErrored (errorMsg) {
    this.errorMsg = errorMsg;
    this.isErrored = true;
  }

  /**
   * Set up HiGlass listeners
   *
   * @param {object} api - HiGlass public API.
   */
  initApi (api) {
    this.config.views.forEach((view) => {
      this.locationTracker[view.uid] = {
        callback: this.trackGenomicLocation(view.uid)
      };

      if (view.trackLocation) {
        this.api.on(
          'location',
          view.uid,
          this.locationTracker[view.uid].callback,
          (id) => { this.locationTracker[view.uid].id = id; }
        );
      }
    });
  }

  /**
   * Handles changes of interaction.
   */
  interactionsChangeHandler () {
    this.store.dispatch(setInteractions(!this.interactions));

    return true;
  }

  /**
   * Load chromosom size infos
   *
   * @param {string} chromInfoUrl - Chromosom size info path.
   */
  loadChromInfo (chromInfoUrl) {
    json(chromInfoUrl, (error, chromInfo) => {
      if (error) { logger.error(error); }

      this.chromInfo = chromInfo;
    });
  }

  /**
   * Track genome locations
   *
   * @param {string} viewId - ID of the view to be tracked.
   */
  trackGenomicLocation (viewId) {
    return (location) => {
      if (
        this.location === location ||
        arraysEqual(this.location, location)
      ) { return; }

      this.location = location;

      if (!this.chromInfo) { return; }

      // Get global locations
      const xStart = this.chromInfo[location[0]].offset + location[1];
      const xEnd = this.chromInfo[location[2]].offset + location[3];
      const yStart = this.chromInfo[location[4]].offset + location[5];
      const yEnd = this.chromInfo[location[6]].offset + location[7];

      // Update state
      this.store.dispatch(setSelectionView([
        xStart,
        xEnd,
        yStart,
        yEnd
      ]));
    };
  }

  update () {
    try {
      const state = this.store.getState().present.decompose;

      const update = {};

      this.checkColumnsDb(state.columns, update);
      this.updateConfig(state.higlass.config, update);
      this.updateInteractions(
        state.higlass.interactions, update, update.render
      );
      this.updateGrayscale(state.higlass.grayscale, update, update.render);
      this.updateFragmentsHighlight(
        state.higlass.fragmentsHighlight,
        state.fragments.config,
        update,
        update.render
      );
      this.updateSelectionView(state.higlass.selectionView, update);

      if (update.render) {
        this.renderDb(this.config);
      }
    } catch (e) {
      logger.error('State is invalid', e);
    }
  }

  updateConfig (config, update) {
    if (
      this.originalConfig === config ||
      Object.keys(config).length === 0
    ) { return; }

    this.originalConfig = config;
    this.config = deepClone(config);

    if (!this.chromInfo && this.config.chromInfoPath) {
      this.loadChromInfo(this.config.chromInfoPath);
    }

    this.config.views.forEach((view, index) => {
      if (view.selectionView) {
        this.selectionViewId = index;
      }
    });

    update.render = true;
  }

  updateInteractions (interactions, update, force) {
    if (
      (this.interactions === interactions && !force) &&
      this.config &&
      this.config.views.length === 1
    ) { return; }

    this.interactions = interactions;

    // this.config.zoomFixed = !this.interactions;

    update.render = true;
  }

  updateGrayscale (grayscale, update, force) {
    if (
      (this.grayscale === grayscale && !force) &&
      this.config
    ) { return; }

    this.grayscale = grayscale;

    this.config.views.forEach((view, index) => {
      if (grayscale) {
        view.tracks.center[0].contents[0].options.colorRange =
          GRAYSCALE_COLORS;
      } else {
        view.tracks.center[0].contents[0].options.colorRange =
          this.originalColoring.views[index]
            .tracks.center[0].contents[0].options.colorRange.slice();
      }
    });

    update.render = true;
  }

  updateFragmentsHighlight (fgmHighlight, fgmConfig, update, force) {
    if (
      (this.fragmentsHighlight === fgmHighlight && !force) &&
      this.config
    ) { return; }

    this.fragmentsHighlight = fgmHighlight;

    this.config.views
      .filter(view => view.tracks.center.length > 1)
      .forEach((view) => {
        const last = view.tracks.center.length - 1;

        if (view.tracks.center[last].type === '2d-chromosome-annotations') {
          view.tracks.center.pop();
        }
      });

    if (this.fragmentsHighlight) {
      const loci = this.extractLoci(fgmConfig);

      if (this.loci !== loci) {
        this.loci = loci;
        this.loci2dTrack = {
          uid: '2d',
          type: '2d-chromosome-annotations',
          chromInfoPath: '//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv',
          options: {
            minRectWidth: 2,
            minRectHeight: 2,
            regions: loci
          }
        };
      }

      this.config.views.forEach((view) => {
        const _loci = deepClone(this.loci2dTrack);
        _loci.uid = `${view.uid}.2d`;

        view.tracks.center.push(_loci);
      });
    }

    update.render = true;
  }

  updateSelectionView (selectionViewDomains, update) {
    if (
      (
        this.selectionViewDomains === selectionViewDomains ||
        typeof this.selectionViewId === 'undefined'
      ) &&
      this.config
    ) { return; }

    this.selectionViewDomains = selectionViewDomains;

    this.config.views[this.selectionViewId].initialXDomain = [
      selectionViewDomains[0],
      selectionViewDomains[1]
    ];

    this.config.views[this.selectionViewId].initialYDomain = [
      selectionViewDomains[2],
      selectionViewDomains[3]
    ];
  }

  render (config) {
    this.areServersAvailable(config)
      .then(() => {
        hg(
          this.plotEl,
          deepClone(config),
          OPTIONS,
          (api) => { this.api = api; }
        );

        this.initApi(this.api);
      })
      .catch((error) => {
        this.hasErrored('Server not available');
      });
  }
}
<commit_msg>Do not update when config is not available<commit_after>// Aurelia
import { inject, LogManager } from 'aurelia-framework';

// Third party
import { json } from 'd3';

// Injectables
import States from 'services/states';

// Utils etc.
import $ from 'utils/dom-el';
import debounce from 'utils/debounce';
import ping from 'utils/ping';
import { createHgComponent as hg } from 'hglib';
import { requestNextAnimationFrame } from 'utils/request-animation-frame';
import {
  setGrayscale,
  setFragmentsHighlight,
  setInteractions,
  setSelectionView
} from 'components/higlass/higlass-actions';
import {
  GRAYSCALE_COLORS
} from 'components/higlass/higlass-defaults';

import arraysEqual from 'utils/arrays-equal';
import deepClone from 'utils/deep-clone';

const logger = LogManager.getLogger('higlass');

const OPTIONS = {
  bounded: true
};

@inject(States)
export class Higlass {
  constructor (states) {
    // Link the Redux store
    this.store = states.store;
    this.store.subscribe(this.update.bind(this));

    this.renderDb = debounce(this.render.bind(this), 50);
    this.checkColumnsDb = debounce(this.checkColumns.bind(this), 150);

    this.isLoading = true;

    this.locationTracker = {};
  }

  attached () {
    this.update();

    setTimeout(() => { this.isLoading = false; }, 150);
  }

  /* ----------------------- Getter / Setter Variables ---------------------- */

  get isErrored () {
    return this._isErrored;
  }

  set isErrored (value) {
    if (!value) {
      this.errorMsg = undefined;
    }

    this._isErrored = value;
  }

  /* ---------------------------- Custom Methods ---------------------------- */

  areServersAvailable (config) {
    let servers;

    try {
      // This needs refactoring but we need to wait until Higlass's config has
      // been refactored.
      servers = [config.views[0].tracks.center[0].contents[0].server];
    } catch (e) {
      return Promise.reject(Error('Broken config'));
    }

    return Promise.all(servers.map(server => ping(server)));
  }

  checkColumns (newColumns) {
    if (this.columns !== newColumns) {
      this.columns = newColumns;

      // For HiGlass to rerender
      requestNextAnimationFrame(() => {
        $(window).dispatch('resize', 'HTMLEvents');
      });
    }
  }

  /**
   * Extract loci in modified BEDPE format for higlass.
   *
   * @param {object} fgmConfig - Fragment config.
   * @return {array} List of loci.
   */
  extractLoci (fgmConfig) {
    const dataIdxChrom1 = fgmConfig.fragmentsHeader.indexOf('chrom1');
    const dataIdxStart1 = fgmConfig.fragmentsHeader.indexOf('start1');
    const dataIdxEnd1 = fgmConfig.fragmentsHeader.indexOf('end1');
    const dataIdxChrom2 = fgmConfig.fragmentsHeader.indexOf('chrom2');
    const dataIdxStart2 = fgmConfig.fragmentsHeader.indexOf('start2');
    const dataIdxEnd2 = fgmConfig.fragmentsHeader.indexOf('end2');

    return fgmConfig.fragments.map(fragment => [
      `chr${fragment[dataIdxChrom1]}`,
      fragment[dataIdxStart1],
      fragment[dataIdxEnd1],
      `chr${fragment[dataIdxChrom2]}`,
      fragment[dataIdxStart2],
      fragment[dataIdxEnd2],
      'rgba(255, 85, 0, 0.8)'
    ]);
  }

  /**
   * Handles changes of highlighting fragments.
   */
  fragmentsHighlightChangeHandler () {
    this.store.dispatch(setFragmentsHighlight(!this.fragmentsHighlight));

    return true;
  }

  /**
   * Handles changes of grayscale.
   */
  grayscaleChangeHandler () {
    this.store.dispatch(setGrayscale(!this.grayscale));

    return true;
  }

  /**
   * Helper method for showing an error.
   *
   * @param {string} errorMsg - Error message to be displayed.
   */
  hasErrored (errorMsg) {
    this.errorMsg = errorMsg;
    this.isErrored = true;
  }

  /**
   * Set up HiGlass listeners
   *
   * @param {object} api - HiGlass public API.
   */
  initApi (api) {
    this.config.views.forEach((view) => {
      this.locationTracker[view.uid] = {
        callback: this.trackGenomicLocation(view.uid)
      };

      if (view.trackLocation) {
        this.api.on(
          'location',
          view.uid,
          this.locationTracker[view.uid].callback,
          (id) => { this.locationTracker[view.uid].id = id; }
        );
      }
    });
  }

  /**
   * Handles changes of interaction.
   */
  interactionsChangeHandler () {
    this.store.dispatch(setInteractions(!this.interactions));

    return true;
  }

  /**
   * Load chromosom size infos
   *
   * @param {string} chromInfoUrl - Chromosom size info path.
   */
  loadChromInfo (chromInfoUrl) {
    json(chromInfoUrl, (error, chromInfo) => {
      if (error) { logger.error(error); }

      this.chromInfo = chromInfo;
    });
  }

  /**
   * Track genome locations
   *
   * @param {string} viewId - ID of the view to be tracked.
   */
  trackGenomicLocation (viewId) {
    return (location) => {
      if (
        this.location === location ||
        arraysEqual(this.location, location)
      ) { return; }

      this.location = location;

      if (!this.chromInfo) { return; }

      // Get global locations
      const xStart = this.chromInfo[location[0]].offset + location[1];
      const xEnd = this.chromInfo[location[2]].offset + location[3];
      const yStart = this.chromInfo[location[4]].offset + location[5];
      const yEnd = this.chromInfo[location[6]].offset + location[7];

      // Update state
      this.store.dispatch(setSelectionView([
        xStart,
        xEnd,
        yStart,
        yEnd
      ]));
    };
  }

  update () {
    try {
      const state = this.store.getState().present.decompose;

      const update = {};

      this.checkColumnsDb(state.columns, update);
      this.updateConfig(state.higlass.config, update);
      this.updateInteractions(
        state.higlass.interactions, update, update.render
      );
      this.updateGrayscale(state.higlass.grayscale, update, update.render);
      this.updateFragmentsHighlight(
        state.higlass.fragmentsHighlight,
        state.fragments.config,
        update,
        update.render
      );
      this.updateSelectionView(state.higlass.selectionView, update);

      if (update.render) {
        this.renderDb(this.config);
      }
    } catch (e) {
      logger.error('State is invalid', e);
    }
  }

  updateConfig (config, update) {
    if (
      this.originalConfig === config ||
      Object.keys(config).length === 0
    ) { return; }

    this.originalConfig = config;
    this.config = deepClone(config);

    if (!this.chromInfo && this.config.chromInfoPath) {
      this.loadChromInfo(this.config.chromInfoPath);
    }

    this.config.views.forEach((view, index) => {
      if (view.selectionView) {
        this.selectionViewId = index;
      }
    });

    update.render = true;
  }

  updateInteractions (interactions, update, force) {
    if (
      (this.interactions === interactions && !force) ||
      !this.config ||
      this.config.views.length === 1
    ) { return; }

    this.interactions = interactions;

    // this.config.zoomFixed = !this.interactions;

    update.render = true;
  }

  updateGrayscale (grayscale, update, force) {
    if (
      (this.grayscale === grayscale && !force) ||
      !this.config
    ) { return; }

    this.grayscale = grayscale;

    console.log(this.config);

    this.config.views.forEach((view, index) => {
      if (grayscale) {
        view.tracks.center[0].contents[0].options.colorRange =
          GRAYSCALE_COLORS;
      } else {
        view.tracks.center[0].contents[0].options.colorRange =
          this.originalColoring.views[index]
            .tracks.center[0].contents[0].options.colorRange.slice();
      }
    });

    update.render = true;
  }

  updateFragmentsHighlight (fgmHighlight, fgmConfig, update, force) {
    if (
      (this.fragmentsHighlight === fgmHighlight && !force) ||
      !this.config
    ) { return; }

    this.fragmentsHighlight = fgmHighlight;

    this.config.views
      .filter(view => view.tracks.center.length > 1)
      .forEach((view) => {
        const last = view.tracks.center.length - 1;

        if (view.tracks.center[last].type === '2d-chromosome-annotations') {
          view.tracks.center.pop();
        }
      });

    if (this.fragmentsHighlight) {
      const loci = this.extractLoci(fgmConfig);

      if (this.loci !== loci) {
        this.loci = loci;
        this.loci2dTrack = {
          uid: '2d',
          type: '2d-chromosome-annotations',
          chromInfoPath: '//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv',
          options: {
            minRectWidth: 2,
            minRectHeight: 2,
            regions: loci
          }
        };
      }

      this.config.views.forEach((view) => {
        const _loci = deepClone(this.loci2dTrack);
        _loci.uid = `${view.uid}.2d`;

        view.tracks.center.push(_loci);
      });
    }

    update.render = true;
  }

  updateSelectionView (selectionViewDomains, update) {
    if (
      (
        this.selectionViewDomains === selectionViewDomains ||
        typeof this.selectionViewId === 'undefined'
      ) ||
      !this.config
    ) { return; }

    this.selectionViewDomains = selectionViewDomains;

    this.config.views[this.selectionViewId].initialXDomain = [
      selectionViewDomains[0],
      selectionViewDomains[1]
    ];

    this.config.views[this.selectionViewId].initialYDomain = [
      selectionViewDomains[2],
      selectionViewDomains[3]
    ];
  }

  render (config) {
    this.areServersAvailable(config)
      .then(() => {
        hg(
          this.plotEl,
          deepClone(config),
          OPTIONS,
          (api) => { this.api = api; }
        );

        this.initApi(this.api);
      })
      .catch((error) => {
        this.hasErrored('Server not available');
      });
  }
}
<|endoftext|>


<commit_before>var express = require('express');
var fs = require('fs')
var path = require('path')
var router = express.Router();

function validate(email) {
  var tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-?\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
  if (!email) return false;

  if (email.length>254) return false;

  var valid = tester.test(email);
  if(!valid) return false;

  var parts = email.split('@');
  if(parts[0].length>64) return false;

  var domainParts = parts[1].split(".");
	if(domainParts.some(function(part) {return part.length>63; }))
		return false;

	return true;
}

router.get('/', function(req, res, next) {
  fs.readFile(path.join(__dirname, '../data/users.json'), function(err, data) {
    if (err) throw err;
    var data = JSON.parse(data)
    var secreat = {
      'users': []
    }
    data.users.forEach(function(user) {
      var secreatUser = '';
      for(index in user) {
        if (index > 2) {
          secreatUser += '*';
        } else {
          secreatUser += user[index];
        }
      }
      secreat.users.push(secreatUser)
    })
    res.send(secreat)
  });
});

router.post('/add', function(req, res, next) {
  fs.readFile(path.join(__dirname, '../data/users.json'), function(err, data) {
    if (err) throw err;
    var data = JSON.parse(data);
    var status = null;
    var info   = null;
    if(validate(req.body.email)) {
      if(data.users.indexOf(req.body.email) > -1) {
        status = 'Fail';
        info   = 'You already subscribed!';
      } else {
        data.users.push(req.body.email);
        fs.writeFile(path.join(__dirname, '../data/users.json'), JSON.stringify(data), function(err) {
          if (err) throw err;
          console.log('file has been updated');
        })
        status = 'Success';
        info   = 'You has become our user!';
      }
    } else {
      status = 'fail';
      info   = 'Your email is not valid!'
    }
    res.send({
      'status': status,
      'info': info
    })
  });
});

router.post('/del', function(req, res, next) {
  fs.readFile(path.join(__dirname, '../data/users.json'), function(err, data) {
    if (err) throw err;
    var data = JSON.parse(data);
    var status = null;
    var info   = null;

    if(validate(req.body.email)) {
      if(data.users.indexOf(req.body.email) > -1) {
        data.users.splice(data.users.indexOf(req.body.email), 1)
        fs.writeFile(path.join(__dirname, '../data/users.json'), JSON.stringify(data), function(err) {
          if (err) throw err;
          console.log('file has been updated');
        })
        status = 'Success';
        info   = 'You have unsubscribed mail notification.';
      } else {
        status = 'Fail';
        info   = 'You are not subscribed yet.';
      }
    } else {
      status = 'Fail';
      info   = 'Your email is not valid!';
    }
    res.send({
      'status': status,
      'info': info
    })
  })
});

module.exports = router;
<commit_msg>(fix): fix async write to sync write<commit_after>var express = require('express');
var fs = require('fs')
var path = require('path')
var router = express.Router();

function validate(email) {
  var tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-?\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
  if (!email) return false;

  if (email.length>254) return false;

  var valid = tester.test(email);
  if(!valid) return false;

  var parts = email.split('@');
  if(parts[0].length>64) return false;

  var domainParts = parts[1].split(".");
	if(domainParts.some(function(part) {return part.length>63; }))
		return false;

	return true;
}

router.get('/', function(req, res, next) {
  fs.readFile(path.join(__dirname, '../data/users.json'), function(err, data) {
    if (err) throw err;
    var data = JSON.parse(data)
    var secreat = {
      'users': []
    }
    data.users.forEach(function(user) {
      var secreatUser = '';
      for(index in user) {
        if (index > 2) {
          secreatUser += '*';
        } else {
          secreatUser += user[index];
        }
      }
      secreat.users.push(secreatUser)
    })
    res.send(secreat)
  });
});

router.post('/add', function(req, res, next) {
  fs.readFile(path.join(__dirname, '../data/users.json'), function(err, data) {
    if (err) throw err;
    var data = JSON.parse(data);
    var status = null;
    var info   = null;
    if(validate(req.body.email)) {
      if(data.users.indexOf(req.body.email) > -1) {
        status = 'Fail';
        info   = 'You already subscribed!';
      } else {
        data.users.push(req.body.email);
        fs.writeFileSync(path.join(__dirname, '../data/users.json'), JSON.stringify(data));
        status = 'Success';
        info   = 'You has become our user!';
      }
    } else {
      status = 'Fail';
      info   = 'Your email is not valid!'
    }
    res.send({
      'status': status,
      'info': info
    })
  });
});

router.post('/del', function(req, res, next) {
  fs.readFile(path.join(__dirname, '../data/users.json'), function(err, data) {
    if (err) throw err;
    var data = JSON.parse(data);
    var status = null;
    var info   = null;

    if(validate(req.body.email)) {
      if(data.users.indexOf(req.body.email) > -1) {
        data.users.splice(data.users.indexOf(req.body.email), 1)
        fs.writeFileSync(path.join(__dirname, '../data/users.json'), JSON.stringify(data));
        status = 'Success';
        info   = 'You have unsubscribed mail notification.';
      } else {
        status = 'Fail';
        info   = 'You are not subscribed yet.';
      }
    } else {
      status = 'Fail';
      info   = 'Your email is not valid!';
    }
    res.send({
      'status': status,
      'info': info
    })
  })
});

module.exports = router;
<|endoftext|>


<commit_before>export default ['$scope', 'Refresh', 'tagSearchService',
    function($scope, Refresh, tagSearchService) {
        // JSONify passed field elements that can be searched
        $scope.list = angular.fromJson($scope.list);
        // Access config lines from list spec
        $scope.listConfig = $scope.$parent.list;
        // Grab options for the left-dropdown of the searchbar
        tagSearchService.getSearchTypes($scope.list, $scope.endpoint)
            .then(function(searchTypes) {
                $scope.searchTypes = searchTypes;

                // currently selected option of the left-dropdown
                $scope.currentSearchType = $scope.searchTypes[0];
            });

        // shows/hide the search type dropdown
        $scope.toggleTypeDropdown = function() {
            if ($scope.searchTypes.length > 1) {
                $scope.showTypeDropdown = !$scope.showTypeDropdown;
                if ($scope.showTypeDropdown) {
                    $("body").append("

");
                    $(".TagSearch-clickToClose").on("click", function() {
                        $scope.showTypeDropdown = false;
                        $(".TagSearch-clickToClose").remove();
                    });
                }
            }
        };

        // sets the search type dropdown and hides it
        $scope.setSearchType = function($event, type) {
            $scope.currentSearchType = type;
            $scope.showTypeDropdown = false;
            $(".TagSearch-clickToClose").remove();
            $event.stopPropagation();
        };

        // if the current search type uses a list instead
        // of a text input, this show hides that list
        $scope.toggleCurrentSearchDropdown = function() {
            $scope
                .showCurrentSearchDropdown = !$scope
                    .showCurrentSearchDropdown;
            if ($scope.showCurrentSearchDropdown) {
                $("body").append("

");
                $(".TagSearch-clickToClose").on("click", function() {
                    $scope.showCurrentSearchDropdown = false;
                    $(".TagSearch-clickToClose").remove();
                });
            }
};

        $scope.updateSearch = function(tags) {
            var iterator = $scope.iterator;
            var pageSize = $scope
                .$parent[iterator + "_page_size"];
            var searchParams = $scope
                .$parent[iterator + "SearchParams"];
            var set = $scope.set;
            var listScope = $scope.$parent;
            var url = tagSearchService
                .updateFilteredUrl($scope.endpoint, tags, pageSize, searchParams);

            $scope.$parent[iterator + "_active_search"] = true;
            Refresh({
                scope: listScope,
                set: set,
                iterator: iterator,
                url: url
            });

            listScope.$on('PostRefresh', function() {
                if (set === 'notifications') {
                    $scope.$emit('relatednotifications');
                }
            });

            $scope.currentSearchFilters = tags;
        };

        // triggers a refilter of the list with the newTag
        $scope.addTag = function($event, type) {
            var newTag = tagSearchService
                .getTag($scope.currentSearchType,
                    $scope.newSearchTag,
                    type);

            // reset the search bar
            $scope.resetSearchBar();

            // make a clone of the currentSearchFilters
            // array and push the newTag to this array
            var tags = tagSearchService
                .getCurrentTags($scope
                    .currentSearchFilters);

            if (!tagSearchService.isDuplicate(tags, newTag) && !!newTag.name) {
                tags.push(newTag);
                $scope.updateSearch(tags);
            }
            $event.stopPropagation();
        };

        // triggers a refilter of the list without the oldTag
        $scope.deleteTag = function(oldTag) {
            // make a clone of the currentSearchFilters
            // array and remove oldTag from the array
            var tags = tagSearchService
                .getCurrentTags($scope
                    .currentSearchFilters)
                .filter(function(tag) {
                    return tag.url !== oldTag.url;
                });

            $scope.updateSearch(tags);
        };

        // make sure all stateful UI triggers are reset
        $scope.resetSearchBar = function() {
            delete $scope.currentSearchSelectedOption;
            $scope.newSearchTag = null;
            $scope.showTypeDropdown = false;
            $scope.showCurrentSearchDropdown = false;
            $(".TagSearch-clickToClose").remove();
        };
    }];
<commit_msg>speed up toggle by manually applying toggle<commit_after>export default ['$scope', 'Refresh', 'tagSearchService',
    function($scope, Refresh, tagSearchService) {
        // JSONify passed field elements that can be searched
        $scope.list = angular.fromJson($scope.list);
        // Access config lines from list spec
        $scope.listConfig = $scope.$parent.list;
        // Grab options for the left-dropdown of the searchbar
        tagSearchService.getSearchTypes($scope.list, $scope.endpoint)
            .then(function(searchTypes) {
                $scope.searchTypes = searchTypes;

                // currently selected option of the left-dropdown
                $scope.currentSearchType = $scope.searchTypes[0];
            });

        // shows/hide the search type dropdown
        $scope.toggleTypeDropdown = function() {
            if ($scope.searchTypes.length > 1) {
                $scope.showTypeDropdown = !$scope.showTypeDropdown;
                if ($scope.showTypeDropdown) {
                    $("body").append("

");
                    $(".TagSearch-clickToClose").on("click", function() {
                        $scope.$apply(function() {
                            $scope.showTypeDropdown = false;
                        });
                        $(".TagSearch-clickToClose").remove();
                    });
                }
            }
        };

        // sets the search type dropdown and hides it
        $scope.setSearchType = function($event, type) {
            $scope.currentSearchType = type;
            $scope.showTypeDropdown = false;
            $(".TagSearch-clickToClose").remove();
            $event.stopPropagation();
        };

        // if the current search type uses a list instead
        // of a text input, this show hides that list
        $scope.toggleCurrentSearchDropdown = function() {
            $scope
                .showCurrentSearchDropdown = !$scope
                    .showCurrentSearchDropdown;
            if ($scope.showCurrentSearchDropdown) {
                $("body").append("

");
                $(".TagSearch-clickToClose").on("click", function() {
                    $scope.$apply(function() {
                        $scope.showCurrentSearchDropdown = false;
                    });
                    $(".TagSearch-clickToClose").remove();
                });
            }
};

        $scope.updateSearch = function(tags) {
            var iterator = $scope.iterator;
            var pageSize = $scope
                .$parent[iterator + "_page_size"];
            var searchParams = $scope
                .$parent[iterator + "SearchParams"];
            var set = $scope.set;
            var listScope = $scope.$parent;
            var url = tagSearchService
                .updateFilteredUrl($scope.endpoint, tags, pageSize, searchParams);

            $scope.$parent[iterator + "_active_search"] = true;
            Refresh({
                scope: listScope,
                set: set,
                iterator: iterator,
                url: url
            });

            listScope.$on('PostRefresh', function() {
                if (set === 'notifications') {
                    $scope.$emit('relatednotifications');
                }
            });

            $scope.currentSearchFilters = tags;
        };

        // triggers a refilter of the list with the newTag
        $scope.addTag = function($event, type) {
            var newTag = tagSearchService
                .getTag($scope.currentSearchType,
                    $scope.newSearchTag,
                    type);

            // reset the search bar
            $scope.resetSearchBar();

            // make a clone of the currentSearchFilters
            // array and push the newTag to this array
            var tags = tagSearchService
                .getCurrentTags($scope
                    .currentSearchFilters);

            if (!tagSearchService.isDuplicate(tags, newTag) && !!newTag.name) {
                tags.push(newTag);
                $scope.updateSearch(tags);
            }
            $event.stopPropagation();
        };

        // triggers a refilter of the list without the oldTag
        $scope.deleteTag = function(oldTag) {
            // make a clone of the currentSearchFilters
            // array and remove oldTag from the array
            var tags = tagSearchService
                .getCurrentTags($scope
                    .currentSearchFilters)
                .filter(function(tag) {
                    return tag.url !== oldTag.url;
                });

            $scope.updateSearch(tags);
        };

        // make sure all stateful UI triggers are reset
        $scope.resetSearchBar = function() {
            delete $scope.currentSearchSelectedOption;
            $scope.newSearchTag = null;
            $scope.showTypeDropdown = false;
            $scope.showCurrentSearchDropdown = false;
            $(".TagSearch-clickToClose").remove();
        };
    }];
<|endoftext|>


<commit_before>"use strict";

angular.module('arethusa.morph').service('morphLocalStorage', [
  'plugins',
  'arethusaLocalStorage',
  '_',
  function(plugins, arethusaLocalStorage, _) {
    var PREFERENCE_DELIMITER = ';;';
    var PREFERENCE_COUNT_DELIMITER = '@@';
    var LEMMA_POSTAG_DELIMITER = '|-|';
    var self = this;

    this.localStorageKey = 'morph.forms';
    this.preferenceKey = 'morph.prefs';

    this.delimiters = {
      preference: PREFERENCE_DELIMITER,
      count: PREFERENCE_COUNT_DELIMITER,
      lemmaToPostag: LEMMA_POSTAG_DELIMITER
    };

    this.retriever = {
      getData: getData,
      abort: function() {}
    };

    this.addForm = addForm;
    this.addForms = addForms;
    this.removeForm = removeForm;

    this.addPreference = addPreference;
    this.addPreferences = addPreferences;
    this.sortByPreference = sortByPreference;

    this.getForms = getForms;
    this.getPreferences = getPreferences;

    function key(k) {
      return self.localStorageKey + '.' + k;
    }

    function preferenceKey(k) {
      return self.preferenceKey + '.' + k;
    }

    function getData(string, callback) {
      var forms = retrieve(string);
      callback(forms);
    }

    function retrieve(string) {
      return arethusaLocalStorage.get(key(string)) || [];
    }

    function retrievePreference(string) {
      return arethusaLocalStorage.get(preferenceKey(string)) || '';
    }

    function persist(string, value) {
      arethusaLocalStorage.set(key(string), value);
    }

    function persistPreference(string, value) {
      return arethusaLocalStorage.set(preferenceKey(string), value);

    }

    function addForm(string, form) {
      // Check if we already stored info about this word,
      // if not add a need array to the store
      var forms = retrieve(string) || [];

      // Store a copy and set the selected property to false!
      var newForm = angular.copy(form);
      newForm.selected = false;
      forms.push(newForm);
      persist(string, forms);
    }

    function addForms(string, newForms) {
      var forms = retrieve(string) || [];
      var keys = _.map(forms, formToKey);
      _.forEach(newForms, function(form) {
        if (!_.contains(keys, formToKey(form))) {
          forms.push(form);
        }
      });
      persist(string, forms);
    }

    function removeForm(string, form) {
      var forms = retrieve(string);
      if (forms) {
        // find element and remove it, when it's present
        var stored = aU.find(forms, function (otherForm) {
          return self.comparator(form, otherForm);
        });
        if (stored) {
          forms.splice(forms.indexOf(stored), 1);
        }
        persist(string, forms);
      }
    }

    function addPreference(string, form, additor) {
      additor = parseInt(additor) || 1;
      var key = formToKey(form);
      var counts = preferencesToCounts(string);
      var counter = counts[key];
      var newCount = counter ? counter + additor : 1;
      counts[key] = newCount;
      var sortedCounts = toSortedArray(counts);
      var toStore = _.map(sortedCounts, function(countArr) {
        return countArr[0] + PREFERENCE_COUNT_DELIMITER + countArr[1];
      }).join(PREFERENCE_DELIMITER);

      persistPreference(string, toStore);
    }

    function addPreferences(string, frequencies) {
      var data = frequencies.split(PREFERENCE_DELIMITER);
      return _.forEach(data, function(datum) {
        var formAndCount = datum.split(PREFERENCE_COUNT_DELIMITER);
        var lemmaAndPostag = formAndCount[0].split(LEMMA_POSTAG_DELIMITER);
        var count = formAndCount[1];
        var lemma = lemmaAndPostag[0];
        var postag  = lemmaAndPostag[1];
        addPreference(string, { lemma: lemma, postag: postag }, count);
      });
    }

    function toSortedArray(counts) {
      return _.map(counts, function(v, k) {
        return [k, v];
      }).sort(function(a, b) {
        return a[1] < b[1];
      });
    }

    function preferencesToCounts(string) {
      var prefs = retrievePreference(string).split(PREFERENCE_DELIMITER);
      return _.inject(_.filter(prefs), function(memo, pref) {
        var parts = pref.split(PREFERENCE_COUNT_DELIMITER);
        memo[parts[0]] = parseInt(parts[1]);
        return memo;
      }, {});
    }

    // Might be better to do this in an immutable way, but it works suprisingly well
    function sortByPreference(string, forms) {
      var counts = preferencesToCounts(string);
      var selectors = _.inject(forms, function(memo, form) {
        memo[formToKey(form)] = form;
        return memo;
      }, {});

      _.forEachRight(toSortedArray(counts), function(counter) {
        var form = selectors[counter[0]];
        if (form) {
          var i = forms.splice(forms.indexOf(form), 1);
          forms.unshift(form);
        }
      });
      return forms;
    }

    function formToKey(form) {
      return form.lemma + LEMMA_POSTAG_DELIMITER + form.postag;
    }

    function getForms() {
      return collectFromStore(self.localStorageKey);
    }

    function getPreferences() {
      return collectFromStore(self.preferenceKey);
    }

    function collectFromStore(keyFragment) {
      return _.inject(arethusaLocalStorage.keys(), function(memo, key) {
        var match = key.match('^' + keyFragment + '.(.*)');
        if (match) {
          memo[match[1]] = arethusaLocalStorage.get(key);
        }
        return memo;
      }, {});
    }
  }
]);
<commit_msg>documenting morph_local_storage service<commit_after>"use strict";

/**
 * @ngdoc service
 * @name arethusa.morph.morph_local_storage
 *
 * @description
 * Manages local storage of a user's morphological forms
 * and preferences
 *
 *
 * @requires arethusa.core.local_storage
 * @requires arethusa.core.plugins
 * @requires lodash.underscore
 */

angular.module('arethusa.morph').service('morphLocalStorage', [
  'plugins',
  'arethusaLocalStorage',
  '_',
  function(plugins, arethusaLocalStorage, _) {
    var PREFERENCE_DELIMITER = ';;';
    var PREFERENCE_COUNT_DELIMITER = '@@';
    var LEMMA_POSTAG_DELIMITER = '|-|';
    var self = this;

    this.localStorageKey = 'morph.forms';
    this.preferenceKey = 'morph.prefs';

    this.delimiters = {
      preference: PREFERENCE_DELIMITER,
      count: PREFERENCE_COUNT_DELIMITER,
      lemmaToPostag: LEMMA_POSTAG_DELIMITER
    };

    /**
     * @ngdoc property
     * @name arethusa.morph.morph_local_storage.retriever
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Encaspulated Arethusa Retriever Implementation
     *
     */
    this.retriever = {
      getData: getData,
      abort: function() {}
    };

    this.addForm = addForm;
    this.addForms = addForms;
    this.removeForm = removeForm;

    this.addPreference = addPreference;
    this.addPreferences = addPreferences;
    this.sortByPreference = sortByPreference;

    this.getForms = getForms;
    this.getPreferences = getPreferences;

    // formats a key for local storage of forms
    function key(k) {
      return self.localStorageKey + '.' + k;
    }

    // formats a key for local storage of preferences
    function preferenceKey(k) {
      return self.preferenceKey + '.' + k;
    }

    // required method of an Arethusa retriever
    // executes callback after retrieving the data
    // requested by the string
    // only used to retrieve form data not preference
    // data
    function getData(string, callback) {
      var forms = retrieve(string);
      callback(forms);
    }

    // retrieve form data from local storage
    function retrieve(string) {
      return arethusaLocalStorage.get(key(string)) || [];
    }

    // retrieve preference data from local storage
    function retrievePreference(string) {
      return arethusaLocalStorage.get(preferenceKey(string)) || '';
    }

    // persist form data to local storage
    function persist(string, value) {
      arethusaLocalStorage.set(key(string), value);
    }

    // persist form preference data to local storage
    function persistPreference(string, value) {
      return arethusaLocalStorage.set(preferenceKey(string), value);

    }

    /**
     * @ngdoc function
     * @name arethusa.morph.morph_local_storage.addForm
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Adds a new morphological form to local storage
     *
     * @param {String} string the form as a string
     * @param {Object} form the morphological form properties
     *
     */
    function addForm(string, form) {
      // Check if we already stored info about this word,
      // if not add a need array to the store
      var forms = retrieve(string) || [];

      // Store a copy and set the selected property to false!
      var newForm = angular.copy(form);
      newForm.selected = false;
      forms.push(newForm);
      persist(string, forms);
    }

    /**
     * @ngdoc function
     * @name arethusa.morph.morph_local_storage.addForms
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Adds a set of morphological forms to local storage
     *
     * @param {String} string the form as a string
     * @param {Array} forms a list of objects containing
     *                the morphological form properties
     *
     */
    function addForms(string, newForms) {
      var forms = retrieve(string) || [];
      var keys = _.map(forms, formToKey);
      _.forEach(newForms, function(form) {
        if (!_.contains(keys, formToKey(form))) {
          forms.push(form);
        }
      });
      persist(string, forms);
    }

    /**
     * @ngdoc function
     * @name arethusa.morph.morph_local_storage.removeForm
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Removes a form from local storage
     *
     * @param {String} string the form as a string
     * @param {Object} form morphological properties
     *                 to remove
     *
     */
    function removeForm(string, form) {
      var forms = retrieve(string);
      if (forms) {
        // find element and remove it, when it's present
        var stored = aU.find(forms, function (otherForm) {
          return self.comparator(form, otherForm);
        });
        if (stored) {
          forms.splice(forms.indexOf(stored), 1);
        }
        persist(string, forms);
      }
    }

    /**
     * @ngdoc function
     * @name arethusa.morph.morph_local_storage.addPreference
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Adds a user's selection of a given form to the
     * history of their selections of that form, or if it
     * is already there, increments the frequency count
     *
     * @param {String} string the form as a string
     * @param {Object} form the selected morphological properties
     * @param {int} additor Optional - an amount to increment by
     *             (if more than the default count of 1)
     */
    function addPreference(string, form, additor) {
      additor = parseInt(additor) || 1;
      var key = formToKey(form);
      var counts = preferencesToCounts(string);
      var counter = counts[key];
      var newCount = counter ? counter + additor : 1;
      counts[key] = newCount;
      var sortedCounts = toSortedArray(counts);
      var toStore = _.map(sortedCounts, function(countArr) {
        return countArr[0] + PREFERENCE_COUNT_DELIMITER + countArr[1];
      }).join(PREFERENCE_DELIMITER);

      persistPreference(string, toStore);
    }

    /**
     * @ngdoc function
     * @name arethusa.morph.morph_local_storage.addPreferences
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Adds a precomposed set of frequency data for a form
     * to local storage. Currently used when importing data
     * from an external source (such as a downloaded backup).
     *
     * @param {String} string the form as a string
     * @param {String} frequencies the frequency data formatted per requirements
     *
     */
    function addPreferences(string, frequencies) {
      var data = frequencies.split(PREFERENCE_DELIMITER);
      return _.forEach(data, function(datum) {
        var formAndCount = datum.split(PREFERENCE_COUNT_DELIMITER);
        var lemmaAndPostag = formAndCount[0].split(LEMMA_POSTAG_DELIMITER);
        var count = formAndCount[1];
        var lemma = lemmaAndPostag[0];
        var postag  = lemmaAndPostag[1];
        addPreference(string, { lemma: lemma, postag: postag }, count);
      });
    }

    // Sorts frequency data for a form by the counts (most counts first)
    // takes as input an object which maps the morphology properties (string form)
    // to the frequency count (as produced by preferencesToCounts)
    function toSortedArray(counts) {
      return _.map(counts, function(v, k) {
        return [k, v];
      }).sort(function(a, b) {
        return a[1] < b[1];
      });
    }

    // maps morphology properties (string form) to the frequency count
    function preferencesToCounts(string) {
      var prefs = retrievePreference(string).split(PREFERENCE_DELIMITER);
      return _.inject(_.filter(prefs), function(memo, pref) {
        var parts = pref.split(PREFERENCE_COUNT_DELIMITER);
        memo[parts[0]] = parseInt(parts[1]);
        return memo;
      }, {});
    }

    /**
     * @ngdoc function
     * @name arethusa.morph.morph_local_storage.sortByPreference
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Returns user's prior selections of the supplied forms
     * sorted according to the frequency with which those
     * forms were previously selected by the user
     *
     * @param {String} string the form as a string
     * @param {Array} forms a list of forms to check for the supplied
     *                string
     *
     * @return {Array} the supplied forms resorted by frequency
     */
    function sortByPreference(string, forms) {
      var counts = preferencesToCounts(string);
      var selectors = _.inject(forms, function(memo, form) {
        memo[formToKey(form)] = form;
        return memo;
      }, {});

      _.forEachRight(toSortedArray(counts), function(counter) {
        var form = selectors[counter[0]];
        if (form) {
          var i = forms.splice(forms.indexOf(form), 1);
          forms.unshift(form);
        }
      });
      return forms;
    }

    // forms morphological properties as a string for storage
    // in the format <lemma>@@<postag>
    function formToKey(form) {
      return form.lemma + LEMMA_POSTAG_DELIMITER + form.postag;
    }

    /**
     * @ngdoc function
     * @name arethusa.morph.morph_local_storage.getForms
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Retrieves user-created forms from local storage
     *
     * @return {Object} the user-created forms
     */
    function getForms() {
      return collectFromStore(self.localStorageKey);
    }

    /**
     * @ngdoc function
     * @name arethusa.morph.morph_local_storage.gepreferences
     * @methodOf arethusa.morph.morph_local_storage
     *
     * @description
     * Retrieves user's form frequency selections from
     * local storage
     *
     * @return {Object} the user form frequency data
     */
    function getPreferences() {
      return collectFromStore(self.preferenceKey);
    }

    // retrieves morphological  data from local
    // storage - looks for all data which starts with the preference
    // key (morph.prefs. or morph.forms.) and returns them in an object
    // mapping the form string to data (preferences or forms)
    function collectFromStore(keyFragment) {
      return _.inject(arethusaLocalStorage.keys(), function(memo, key) {
        var match = key.match('^' + keyFragment + '.(.*)');
        if (match) {
          memo[match[1]] = arethusaLocalStorage.get(key);
        }
        return memo;
      }, {});
    }
  }
]);
<|endoftext|>


<commit_before>define(function(require) {
  'use strict';

  var states = require('states');
  var modules = require('modules');
  var plugins = require('plugins');
  var _ = require('lodash');
  var angular = require('angular');

  require('autofill-event');
  require('angular-cookies');
  require('angular-resource');
  require('angular-sanitize');
  require('angular-bootstrap');
  require('angular-hotkeys');
  require('moment');
  require('angular-bootstrap-datetimepicker');
  require('angular-ui-router');
  require('angular-translate');
  require('angular-xeditable');
  require('scripts/layout/layout');

  var alien4cloud = modules.get('alien4cloud', [
    'ngCookies',
    'ngResource',
    'ngSanitize',
    'ui.bootstrap',
    'ui.router',
    'pascalprecht.translate',
    'xeditable'
  ]);

  // bootstrap angular js application
  states.state('home', {
    url: '/',
    templateUrl: 'views/main.html',
    controller: ['$scope', 'authService', 'hopscotchService', '$state', function($scope, authService, hopscotchService, $state) {
      $scope.isAdmin = false;
      var isAdmin = authService.hasRole('ADMIN');
      if(_.defined(isAdmin.then)) {
        authService.hasRole('ADMIN').then(function(result) {
          $scope.isAdmin = result;
        });
      } else {
        $scope.isAdmin = isAdmin;
      }
      $scope.adminTour = function() {
        $state.go('admin');
        hopscotchService.startTour('admin.home');
      };
    }]
  });
  states.state('restricted', {
    url: '/restricted',
    templateUrl: 'views/authentication/restricted.html'
  });

  require('scripts/common/services/rest_technical_error_interceptor');

  alien4cloud.startup = function() {
    // add requirements to alien4cloud
    modules.link(alien4cloud);

    alien4cloud.config(['$stateProvider', '$urlRouterProvider', '$httpProvider',
      function($stateProvider, $urlRouterProvider, $httpProvider) {
        $httpProvider.interceptors.push('restTechnicalErrorInterceptor');
        $httpProvider.defaults.headers.common['A4C-Agent'] = 'AngularJS_UI';
        $urlRouterProvider.otherwise('/');
        states.config($stateProvider);
      }
    ]);

    // TODO load more modules
    alien4cloud.config(['$translateProvider',
      function($translateProvider) {
        $translateProvider.translations({CODE: 'en-us'});
        // Default language to load
        $translateProvider.preferredLanguage('en-us');

        var options = {
          files: [{
            prefix: 'data/languages/locale-',
            suffix: '.json'
          }]
        };

        // load translations provided by plugins
        if(!_.isEmpty(plugins.registeredTranlations)){
          _.each(plugins.registeredTranlations, function(trans){
            options.files.push({
              prefix: trans.prefix,
              suffix: trans.suffix
            });
          });
        }

        // Static file loader
        $translateProvider.useStaticFilesLoader(options);
      }
    ]);

    alien4cloud.run(['$rootScope', '$state', 'editableOptions', 'editableThemes', 'authService',
      function($rootScope, $state, editableOptions, editableThemes, authService) {
        var statusFetched = false; // flag to know if we have fetched current user status (logged in and roles)
        $rootScope._ = _;
        // check when the state is about to change
        $rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
          if (!statusFetched && _.defined(event)) {
            // we must have the user status before going to a state.
            event.preventDefault();
          }
          authService.getStatus().$promise.then(function() {
            var propagateState = !statusFetched; // if we prevented state change we have to trigger it now that we fetched user status
            statusFetched = true;
            // check all the menu array & permissions
            authService.menu.forEach(function(menuItem) {
              var menuType = menuItem.id.split('.')[1];
              var foundMenuIndex = toState.name.indexOf(menuType);
              if (foundMenuIndex === 0 && menuItem.hasRole === false) {
                $state.go('restricted');
              }
            });
            if (propagateState) {
              // This is needed only if we prevented the default state change (so if user status was'nt fetched).
              $state.go(toState, toParams);
            }
          });
        });

        // state routing
        $rootScope.$on('$stateChangeSuccess', function(event, toState) {
          var forward = states.stateForward[toState.name];
          if (_.defined(forward)) {
            $state.go(forward);
          }
        });

        /* angular-xeditable config */
        editableThemes.bs3.inputClass = 'input-sm';
        editableThemes.bs3.buttonsClass = 'btn-sm';
        editableThemes.bs3.submitTpl = '<button type="button" class="btn btn-primary"' +
          ' confirm="{{\'CONFIRM_MESSAGE\' | translate}}"' +
          ' confirm-title="{{\'CONFIRM\' | translate }}"' +
          ' confirm-placement="left"' +
          ' cancel-handler="$form.$cancel()"' +
          ' ng-click="$event.stopPropagation();">' +
          '' +
          '</button>';
        editableThemes.bs3.cancelTpl = '<button type="button" class="btn btn-default" ng-click="$form.$cancel()">' +
          '' +
          '</button>';
        editableOptions.theme = 'bs3';
      }
    ]);

    angular.bootstrap(document, ['alien4cloud']);
  };

  return alien4cloud;
});
<commit_msg>Use # in url rather than angular 1.6 #! default<commit_after>define(function(require) {
  'use strict';

  var states = require('states');
  var modules = require('modules');
  var plugins = require('plugins');
  var _ = require('lodash');
  var angular = require('angular');

  require('autofill-event');
  require('angular-cookies');
  require('angular-resource');
  require('angular-sanitize');
  require('angular-bootstrap');
  require('angular-hotkeys');
  require('moment');
  require('angular-bootstrap-datetimepicker');
  require('angular-ui-router');
  require('angular-translate');
  require('angular-xeditable');
  require('scripts/layout/layout');

  var alien4cloud = modules.get('alien4cloud', [
    'ngCookies',
    'ngResource',
    'ngSanitize',
    'ui.bootstrap',
    'ui.router',
    'pascalprecht.translate',
    'xeditable'
  ]);

  // bootstrap angular js application
  states.state('home', {
    url: '/',
    templateUrl: 'views/main.html',
    controller: ['$scope', 'authService', 'hopscotchService', '$state', function($scope, authService, hopscotchService, $state) {
      $scope.isAdmin = false;
      var isAdmin = authService.hasRole('ADMIN');
      if(_.defined(isAdmin.then)) {
        authService.hasRole('ADMIN').then(function(result) {
          $scope.isAdmin = result;
        });
      } else {
        $scope.isAdmin = isAdmin;
      }
      $scope.adminTour = function() {
        $state.go('admin');
        hopscotchService.startTour('admin.home');
      };
    }]
  });
  states.state('restricted', {
    url: '/restricted',
    templateUrl: 'views/authentication/restricted.html'
  });

  require('scripts/common/services/rest_technical_error_interceptor');

  alien4cloud.startup = function() {
    // add requirements to alien4cloud
    modules.link(alien4cloud);

    alien4cloud.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', '$locationProvider',
      function($stateProvider, $urlRouterProvider, $httpProvider, $locationProvider) {
        $httpProvider.interceptors.push('restTechnicalErrorInterceptor');
        $httpProvider.defaults.headers.common['A4C-Agent'] = 'AngularJS_UI';
        $urlRouterProvider.otherwise('/');
        states.config($stateProvider);
        $locationProvider.html5Mode(false);
        $locationProvider.hashPrefix('');
      }
    ]);

    // TODO load more modules
    alien4cloud.config(['$translateProvider',
      function($translateProvider) {
        $translateProvider.translations({CODE: 'en-us'});
        // Default language to load
        $translateProvider.preferredLanguage('en-us');

        var options = {
          files: [{
            prefix: 'data/languages/locale-',
            suffix: '.json'
          }]
        };

        // load translations provided by plugins
        if(!_.isEmpty(plugins.registeredTranlations)){
          _.each(plugins.registeredTranlations, function(trans){
            options.files.push({
              prefix: trans.prefix,
              suffix: trans.suffix
            });
          });
        }

        // Static file loader
        $translateProvider.useStaticFilesLoader(options);
      }
    ]);

    alien4cloud.run(['$rootScope', '$state', 'editableOptions', 'editableThemes', 'authService',
      function($rootScope, $state, editableOptions, editableThemes, authService) {
        var statusFetched = false; // flag to know if we have fetched current user status (logged in and roles)
        $rootScope._ = _;
        // check when the state is about to change
        $rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
          if (!statusFetched && _.defined(event)) {
            // we must have the user status before going to a state.
            event.preventDefault();
          }
          authService.getStatus().$promise.then(function() {
            var propagateState = !statusFetched; // if we prevented state change we have to trigger it now that we fetched user status
            statusFetched = true;
            // check all the menu array & permissions
            authService.menu.forEach(function(menuItem) {
              var menuType = menuItem.id.split('.')[1];
              var foundMenuIndex = toState.name.indexOf(menuType);
              if (foundMenuIndex === 0 && menuItem.hasRole === false) {
                $state.go('restricted');
              }
            });
            if (propagateState) {
              // This is needed only if we prevented the default state change (so if user status was'nt fetched).
              $state.go(toState, toParams);
            }
          });
        });

        // state routing
        $rootScope.$on('$stateChangeSuccess', function(event, toState) {
          var forward = states.stateForward[toState.name];
          if (_.defined(forward)) {
            $state.go(forward);
          }
        });

        /* angular-xeditable config */
        editableThemes.bs3.inputClass = 'input-sm';
        editableThemes.bs3.buttonsClass = 'btn-sm';
        editableThemes.bs3.submitTpl = '<button type="button" class="btn btn-primary"' +
          ' confirm="{{\'CONFIRM_MESSAGE\' | translate}}"' +
          ' confirm-title="{{\'CONFIRM\' | translate }}"' +
          ' confirm-placement="left"' +
          ' cancel-handler="$form.$cancel()"' +
          ' ng-click="$event.stopPropagation();">' +
          '' +
          '</button>';
        editableThemes.bs3.cancelTpl = '<button type="button" class="btn btn-default" ng-click="$form.$cancel()">' +
          '' +
          '</button>';
        editableOptions.theme = 'bs3';
      }
    ]);

    angular.bootstrap(document, ['alien4cloud']);
  };

  return alien4cloud;
});
<|endoftext|>


<commit_before>module.exports = {
  'function': function () {},
  'emptyType': new function EmptyType () {},
  'customType': new function CustomType () { this.x = 2 },
  'buffer': new Buffer(0)
}
<commit_msg>tests: add missing ()<commit_after>module.exports = {
  'function': function () {},
  'emptyType': new function EmptyType () {}(),
  'customType': new function CustomType () { this.x = 2 }(),
  'buffer': new Buffer(0)
}
<|endoftext|>


<commit_before>/**
 * Tester for viewer
 */
define(function (require) {
    'use strict';

    var util = require('util');
    var viewer = require('viewer');
    var viewport = require('viewport');

    var docElement = document.documentElement || document.body;
    var ele = document.createElement('a');
    ele.href =  'http://baidu.com';
    ele.id = 'delegateLink';
    docElement.appendChild(ele);

    describe('viewer', function () {
        it('patchForIframe', function () {
            util.platform.needSpecialScroll = false;
            document.body.style.cssText = '';
            document.body.style.margin = 0;
            function checkHeight() {
                return document.body.style.height === '100%';
            };
            viewer.patchForIframe();
            var initedHeightEmpty = !checkHeight();
            util.platform.needSpecialScroll = true;
            viewer.patchForIframe();

            expect(initedHeightEmpty).to.be.true;
            expect(checkHeight()).to.be.true;
        });

        it('needBackReload', function () {
            var stub1 = sinon.stub(util.platform, 'getOsVersion', function () {
                return '9.1.1';
            });
            var stub2 = sinon.stub(util.platform, 'isSafari', function () {
                return true;
            });
            viewer.patchForIframe();
            stub1.restore();
            stub2.restore();

            var eve = document.createEvent("HTMLEvents");
            eve.initEvent("pageshow", true, true);
            window.dispatchEvent(eve);
        });

        it('show', function () {
            viewer.show();

            expect(document.body.style.opacity).to.equal('1');
        });

        it('init', function () {
            viewer.init();
            expect(viewer._gesture).to.be.instanceof(util.Gesture);
        });

        it('sroll up', function () {
            var style = getComputedStyle(document.body);
            var height = style.height;
            viewer.init();
            document.body.style.height = '10000px';
            viewport.setScrollTop(800);
            expect(viewport.getScrollTop()).to.equal(800);
            document.body.style.height = height;
        });

        it('sendMessage', function () {
            viewer.isIframed = false;
            expect(viewer.sendMessage.bind(viewer)).to.not.throw(Error);

            viewer.isIframed = true;
            expect(viewer.sendMessage.bind(viewer)).to.not.throw(Error);
        });

        it('handlePreregisteredExtensions', function () {
            window.MIP.extensions = ['mip-form'];
            viewer.handlePreregisteredExtensions();
            var result = window.MIP.push({
                name: "mip-form",
                func: function() {
                    document.body.classList.add('handlePreregisteredExtensions');
                }
            });
            expect(document.body === document.querySelector('.handlePreregisteredExtensions')).to.be.true;
            document.body.classList.remove('handlePreregisteredExtensions');
        });
    });
});
<commit_msg>add unit test for viewer<commit_after>/**
 * Tester for viewer
 */
define(function (require) {
    'use strict';

    var util = require('util');
    var viewer = require('viewer');
    var viewport = require('viewport');

    var docElement = document.documentElement || document.body;
    var ele = document.createElement('a');
    ele.href =  'http://baidu.com';
    ele.id = 'delegateLink';
    docElement.appendChild(ele);

    describe('viewer', function () {
        it('patchForIframe', function () {
            util.platform.needSpecialScroll = false;
            document.body.style.cssText = '';
            document.body.style.margin = 0;
            function checkHeight() {
                return document.body.style.height === '100%';
            };
            viewer.patchForIframe();
            var initedHeightEmpty = !checkHeight();
            util.platform.needSpecialScroll = true;
            viewer.patchForIframe();

            expect(initedHeightEmpty).to.be.true;
            expect(checkHeight()).to.be.true;
        });

        it('needBackReload', function () {
            var stub1 = sinon.stub(util.platform, 'getOsVersion', function () {
                return '9.1.1';
            });
            var stub2 = sinon.stub(util.platform, 'isSafari', function () {
                return true;
            });
            viewer.patchForIframe();
            stub1.restore();
            stub2.restore();

            var eve = document.createEvent("HTMLEvents");
            eve.initEvent("pageshow", true, true);
            window.dispatchEvent(eve);
        });

        it('show', function () {
            viewer.show();

            expect(document.body.style.opacity).to.equal('1');
        });

        it('init', function () {
            viewer.init();
            expect(viewer._gesture).to.be.instanceof(util.Gesture);
        });

        it('sroll up', function () {
            var style = getComputedStyle(document.body);
            var height = style.height;
            viewer.init();
            document.body.style.height = '10000px';
            viewport.setScrollTop(800);
            expect(viewport.getScrollTop()).to.equal(800);
            document.body.style.height = height;
        });

        it('sendMessage', function () {
            viewer.isIframed = false;
            expect(viewer.sendMessage.bind(viewer)).to.not.throw(Error);

            viewer.isIframed = true;
            expect(viewer.sendMessage.bind(viewer)).to.not.throw(Error);
        });

        it('handlePreregisteredExtensions', function () {
            window.MIP.extensions = ['mip-form'];
            viewer.handlePreregisteredExtensions();
            var result = window.MIP.push({
                name: "mip-form",
                func: function() {
                    document.body.classList.add('handlePreregisteredExtensions');
                }
            });
            expect(document.body === document.querySelector('.handlePreregisteredExtensions')).to.be.true;
            document.body.classList.remove('handlePreregisteredExtensions');
        });

        it('bindEventCallback', function () {
            viewer._bindEventCallback('show', function () {
                document.body.classList.add('_bindEventCallback');
            });
            expect(document.body === document.querySelector('._bindEventCallback')).to.be.true;
            document.body.classList.remove('_bindEventCallback');
        });
    });
});
<|endoftext|>


<commit_before>const { stripIndent } = require('common-tags')

function template7({ profile, schools, jobs, skills, projects }) {
  return stripIndent`
    ${generateHeader()}
    ${generateProfileSection(profile)}
    \\begin{document}
    ${profile ? '\\makecvtitle' : ''}

    \\section{Education}
    \\cventry{Jan 2017}{BA Computer Science}{Rutgers University}{GPA: 3.0}{\\textit{New Brunswick, NJ}}{}  % arguments 3 to 6 can be left empty

    \\section{Experience}
    \\cventry{Jun 2016 -- Aug 2016}{Software Engineer Intern}{Mozilla}{Mountain View, CA}{}{
    \\begin{itemize}%
    \\item Broadened search criteria for Firefox’s context menu to include subdomains in password suggestions.
    \\item Refactored disabled-host APIs to use the permission manager for both Firefox and Android’s Fennec.
    \\end{itemize}}

    \\cventry{Dec 2015 -- May 2016}{Coding Advisor}{Codecademy}{Manhattan, NY}{}{
    \\begin{itemize}%
    \\item Created a JavaScript project for Codecademy Pro members now available in the new JS course.
    \\item Taught new coders how to avoid bugs and how to go through the process of fixing existing ones.
    \\item Reviewed general programming topics with students and provided assistance for lessons in Java, HTML, CSS, JavaScript, and Ruby.
    \\end{itemize}}

    \\cventry{Jun 2015 -- Nov 2015}{Application Developer Intern}{IEEE}{Piscataway, NJ}{}{
    \\begin{itemize}%
    \\item Wrote an API that allowed CRUD operations to be used for accessing and manipulating data involving current departments/groups/teams at IEEE.
    \\item Created a UI for admins that used the aforementioned API to automate the process of syncing departments/groups/teams on the site to relevant databases.
    \\item Improved the IEEE Innovate site by using cookies to display tailored web-content.
    \\end{itemize}}

    \\section{Skills}
    \\cvitem{Languages}{Java, JavaScript, Ruby, Python, \\LaTeX, HTML, CSS}
    \\cvitem{Frameworks}{Node.js, Koa, Express, React, Redux, Sinatra}

    \\section{Projects}
    \\cventry{}{LaTeX Resume Generator}{}{\\textit{Node.js, Koa, React, Redux}}{}{A webapp for generating LaTeX resumes from form data (including this one).\\\\https://latexresu.me}

    \\vspace{2mm}

    \\cventry{}{Flow Timer}{}{\\textit{Node.js, Koa, React, Redux}}{}{A modern speedcubing app with a scrambler, timer, and analyzer for cubing statistics.\\\\https://flowtimer.com}

    \\vspace{2mm}

    \\cventry{}{Reddit Image Scraper}{}{\\textit{Ruby, Sinatra}}{}{A web app that lets you view a collage of images/videos from a subreddit.\\\\reddit-image-scraper.herokuapp.com}

    \\end{document}
  `
}

function generateProfileSection(profile = {}) {
  const { fullName, email, phoneNumber, address, link } = profile

  return stripIndent`
    % Profile
    \\name{${fullName || ''}}{}
    \\address{${address || ''}}
    ${phoneNumber ? `\\phone[mobile]{${phoneNumber}}` : ''}
    ${email ? `\\email{${email || ''}}` : ''}
    ${link ? `\\homepage{${link || ''}}` : ''}
  `
}

function generateHeader() {
  return stripIndent`
    %% start of file 'template.tex'.
    %% Copyright 2006-2013 Xavier Danaux (xdanaux@gmail.com).
    %
    % This work may be distributed and/or modified under the
    % conditions of the LaTeX Project Public License version 1.3c,
    % available at http://www.latex-project.org/lppl/.

    \\documentclass[11pt,a4paper,sans]{moderncv}        % possible options include font size ('10pt', '11pt' and '12pt'), paper size ('a4paper', 'letterpaper', 'a5paper', 'legalpaper', 'executivepaper' and 'landscape') and font family ('sans' and 'roman')

    % moderncv themes
    \\moderncvstyle{classic}                             % style options are 'casual' (default), 'classic', 'oldstyle' and 'banking'
    \\moderncvcolor{blue}                               % color options 'blue' (default), 'orange', 'green', 'red', 'purple', 'grey' and 'black'
    %\\renewcommand{\\familydefault}{\\sfdefault}         % to set the default font; use '\\sfdefault' for the default sans serif font, '\\rmdefault' for the default roman one, or any tex font name
    %\\nopagenumbers{}                                  % uncomment to suppress automatic page numbering for CVs longer than one page

    % character encoding
    \\usepackage[utf8]{inputenc}                       % if you are not using xelatex ou lualatex, replace by the encoding you are using
    %\\usepackage{CJKutf8}                              % if you need to use CJK to typeset your resume in Chinese, Japanese or Korean

    % adjust the page margins
    \\usepackage[scale=0.75]{geometry}
    %\\setlength{\\hintscolumnwidth}{3cm}                % if you want to change the width of the column with the dates
    %\\setlength{\\makecvtitlenamewidth}{10cm}           % for the 'classic' style, if you want to force the width allocated to your name and avoid line breaks. be careful though, the length is normally calculated to avoid any overlap with your personal info; use this at your own typographical risks...
  `
}

module.exports = template7
<commit_msg>add education section to template7<commit_after>const { stripIndent, source } = require('common-tags')

function template7({ profile, schools, jobs, skills, projects }) {
  return stripIndent`
    ${generateHeader()}
    ${generateProfileSection(profile)}
    \\begin{document}
    ${profile ? '\\makecvtitle' : ''}
    ${generateEducationSection(schools)}
    \\section{Experience}
    \\cventry{Jun 2016 -- Aug 2016}{Software Engineer Intern}{Mozilla}{Mountain View, CA}{}{
    \\begin{itemize}%
    \\item Broadened search criteria for Firefox’s context menu to include subdomains in password suggestions.
    \\item Refactored disabled-host APIs to use the permission manager for both Firefox and Android’s Fennec.
    \\end{itemize}}

    \\cventry{Dec 2015 -- May 2016}{Coding Advisor}{Codecademy}{Manhattan, NY}{}{
    \\begin{itemize}%
    \\item Created a JavaScript project for Codecademy Pro members now available in the new JS course.
    \\item Taught new coders how to avoid bugs and how to go through the process of fixing existing ones.
    \\item Reviewed general programming topics with students and provided assistance for lessons in Java, HTML, CSS, JavaScript, and Ruby.
    \\end{itemize}}

    \\cventry{Jun 2015 -- Nov 2015}{Application Developer Intern}{IEEE}{Piscataway, NJ}{}{
    \\begin{itemize}%
    \\item Wrote an API that allowed CRUD operations to be used for accessing and manipulating data involving current departments/groups/teams at IEEE.
    \\item Created a UI for admins that used the aforementioned API to automate the process of syncing departments/groups/teams on the site to relevant databases.
    \\item Improved the IEEE Innovate site by using cookies to display tailored web-content.
    \\end{itemize}}

    \\section{Skills}
    \\cvitem{Languages}{Java, JavaScript, Ruby, Python, \\LaTeX, HTML, CSS}
    \\cvitem{Frameworks}{Node.js, Koa, Express, React, Redux, Sinatra}

    \\section{Projects}
    \\cventry{}{LaTeX Resume Generator}{}{\\textit{Node.js, Koa, React, Redux}}{}{A webapp for generating LaTeX resumes from form data (including this one).\\\\https://latexresu.me}

    \\vspace{2mm}

    \\cventry{}{Flow Timer}{}{\\textit{Node.js, Koa, React, Redux}}{}{A modern speedcubing app with a scrambler, timer, and analyzer for cubing statistics.\\\\https://flowtimer.com}

    \\vspace{2mm}

    \\cventry{}{Reddit Image Scraper}{}{\\textit{Ruby, Sinatra}}{}{A web app that lets you view a collage of images/videos from a subreddit.\\\\reddit-image-scraper.herokuapp.com}

    \\end{document}
  `
}

function generateProfileSection(profile = {}) {
  const { fullName, email, phoneNumber, address, link } = profile

  return stripIndent`
    % Profile
    \\name{${fullName || ''}}{}
    \\address{${address || ''}}
    ${phoneNumber ? `\\phone[mobile]{${phoneNumber}}` : ''}
    ${email ? `\\email{${email || ''}}` : ''}
    ${link ? `\\homepage{${link || ''}}` : ''}
  `
}

function generateEducationSection(schools) {
  if (!schools) {
    return ''
  }

  return source`
    \\section{Education}
    ${schools.map((school) => {
      const { name, degree, major, gpa, location, graduationDate } = school

      let degreeLine = ''

      if (degree && major) {
        degreeLine = `${degree} in ${major}`
      } else if (degree || major) {
        degreeLine = degree || major
      }

      return stripIndent`
        \\cventry{${graduationDate || ''}}{${degreeLine}}{${name || ''}}{${gpa ? `GPA: ${gpa}` : ''}}{\\textit{${location || ''}}}{}
      `
    })}
  `
}

function generateHeader() {
  return stripIndent`
    %% start of file 'template.tex'.
    %% Copyright 2006-2013 Xavier Danaux (xdanaux@gmail.com).
    %
    % This work may be distributed and/or modified under the
    % conditions of the LaTeX Project Public License version 1.3c,
    % available at http://www.latex-project.org/lppl/.

    \\documentclass[11pt,a4paper,sans]{moderncv}        % possible options include font size ('10pt', '11pt' and '12pt'), paper size ('a4paper', 'letterpaper', 'a5paper', 'legalpaper', 'executivepaper' and 'landscape') and font family ('sans' and 'roman')

    % moderncv themes
    \\moderncvstyle{classic}                             % style options are 'casual' (default), 'classic', 'oldstyle' and 'banking'
    \\moderncvcolor{blue}                               % color options 'blue' (default), 'orange', 'green', 'red', 'purple', 'grey' and 'black'
    %\\renewcommand{\\familydefault}{\\sfdefault}         % to set the default font; use '\\sfdefault' for the default sans serif font, '\\rmdefault' for the default roman one, or any tex font name
    %\\nopagenumbers{}                                  % uncomment to suppress automatic page numbering for CVs longer than one page

    % character encoding
    \\usepackage[utf8]{inputenc}                       % if you are not using xelatex ou lualatex, replace by the encoding you are using
    %\\usepackage{CJKutf8}                              % if you need to use CJK to typeset your resume in Chinese, Japanese or Korean

    % adjust the page margins
    \\usepackage[scale=0.75]{geometry}
    %\\setlength{\\hintscolumnwidth}{3cm}                % if you want to change the width of the column with the dates
    %\\setlength{\\makecvtitlenamewidth}{10cm}           % for the 'classic' style, if you want to force the width allocated to your name and avoid line breaks. be careful though, the length is normally calculated to avoid any overlap with your personal info; use this at your own typographical risks...
  `
}

module.exports = template7
<|endoftext|>


<commit_before>/***
 * Copyright (c) 2015, 2016 Alex Grant (@localnerve), LocalNerve LLC
 * Copyrights licensed under the BSD License. See the accompanying LICENSE file for terms.
 *
 * Handlers to fulfill service worker message commands.
 */
/* global Promise */
'use strict';

var backgrounds = require('./backgrounds');
var routes = require('./routes');
var update = require('./update');
var apiRequests = require('./apiRequests');
var stores = require('../utils/db').init({ key: 'stores' });
var apis = require('../utils/db').init({ key: 'apis' });
var timestamp = require('../utils/db').init({ key: 'timestamp' });
var debug = require('../utils/debug')('init');

/**
 * Run the 'init' sequence.
 *
 * Uses the initial store data sent from the server to setup dynamic request
 * handling, and to keep the store data up-to-date if the app is online.
 *
 * When?
 * 1. Gets executed every new app load (once per session), via message.
 * 2. Gets executed at the beginning of service worker start, via load.
 * So can run multiple times, *must be idempotent*.
 *
 * What?
 * 1. Updates the init.stores in IndexedDB if the app is online.
 * 2. Installs background fetch handling.
 * 3. Installs api request fetch handling.
 * 4. Installs route fetch handling.
 * 5. Precaches/prefetches backgrounds and routes.
 *
 * TODO: Add sync.serviceAllRequests to maintain deferred api requests (#15).
 *
 * @param {Object} payload - Initial payload
 * @param {Number} payload.timestamp - The timestamp of the payload.
 * @param {Object} payload.stores - The flux stores for the app.
 * @param {Object} payload.apis - The api information for the app.
 * @param {Boolean} payload.startup - Indicates the sw started up and memory
 * needs initializing.
 * @param {Function} responder - Function to call to resolve the message
 */
function init (payload, responder) {
  debug('Running init, payload:', payload);

  return update(payload).then(function (updated) {
    if (updated || payload.startup) {
      return backgrounds(payload.stores)
        .then(function () {
          return apiRequests(payload.apis);
        })
        .then(function () {
          return routes(payload.stores);
        });
    } else {
      debug('init skipped');
      return Promise.resolve();
    }
  })
  .then(function () {
    responder({
      error: null
    });
  })
  .catch(function (error) {
    debug('init failed', error);
    responder({
      error: error.toString()
    });
  });
}

/**
 * Reads all the stored init data.
 *
 * @return {Promise} A Promise that resolves to a Object with the init payload.
 */
function initData () {
  return Promise.all([
    stores.read(),
    apis.read(),
    timestamp.read()
  ]).then(function (data) {
    return {
      stores: data[0],
      apis: data[1],
      timestamp: data[2]
    };
  });
}

/**
 * Expose the init command and public storage access for init things.
 */
module.exports = {
  command: init,
  data: initData
};
<commit_msg>fix #15, connect serviceAllRequests to init<commit_after>/***
 * Copyright (c) 2015, 2016 Alex Grant (@localnerve), LocalNerve LLC
 * Copyrights licensed under the BSD License. See the accompanying LICENSE file for terms.
 *
 * Handlers to fulfill service worker message commands.
 */
/* global Promise */
'use strict';

var backgrounds = require('./backgrounds');
var routes = require('./routes');
var update = require('./update');
var apiRequests = require('./apiRequests');
var sync = require('../sync');
var stores = require('../utils/db').init({ key: 'stores' });
var apis = require('../utils/db').init({ key: 'apis' });
var timestamp = require('../utils/db').init({ key: 'timestamp' });
var debug = require('../utils/debug')('init');

/**
 * Kick-off the maintenance and synchronization of stored requests.
 */
function startRequestSync () {
  sync.serviceAllRequests()
  .then(function (results) {
    results.forEach(function (result) {
      if (result && result.failureCount) {
        debug('WOULD manage abandoned request', result);
      }
    });
  })
  .catch(function (error) {
    debug('serviceAllRequests failed ', error);
  });
}

/**
 * Run the 'init' sequence.
 *
 * Uses the initial store data sent from the server to setup dynamic request
 * handling, and to keep the store data up-to-date if the app is online.
 *
 * When?
 * 1. Gets executed every new app load (once per session), via message.
 * 2. Gets executed at the beginning of service worker start, via load.
 * So can run multiple times, *must be idempotent*.
 *
 * What?
 * 0. Synchronizes/Maintains stored requests.
 * 1. Updates the init.stores in IndexedDB if the app is online.
 * 2. Installs background fetch handling.
 * 3. Installs api request fetch handling.
 * 4. Installs route fetch handling.
 * 5. Precaches/prefetches backgrounds and routes.
 *
 * TODO: Add sync.serviceAllRequests to maintain deferred api requests (#15).
 *
 * @param {Object} payload - Initial payload
 * @param {Number} payload.timestamp - The timestamp of the payload.
 * @param {Object} payload.stores - The flux stores for the app.
 * @param {Object} payload.apis - The api information for the app.
 * @param {Boolean} payload.startup - Indicates the sw started up and memory
 * needs initializing.
 * @param {Function} responder - Function to call to resolve the message
 */
function init (payload, responder) {
  debug('Running init, payload:', payload);

  // As a side-effect, asynchronously maintain stored requests.
  // TODO: Move to 'sync' event handler.
  startRequestSync();

  // Update stores and setup route mappings.
  return update(payload).then(function (updated) {
    if (updated || payload.startup) {
      return backgrounds(payload.stores)
        .then(function () {
          return apiRequests(payload.apis);
        })
        .then(function () {
          return routes(payload.stores);
        });
    } else {
      debug('init skipped');
      return Promise.resolve();
    }
  })
  .then(function () {
    responder({
      error: null
    });
  })
  .catch(function (error) {
    debug('init failed', error);
    responder({
      error: error.toString()
    });
  });
}

/**
 * Reads all the stored init data.
 *
 * @return {Promise} A Promise that resolves to a Object with the init payload.
 */
function initData () {
  return Promise.all([
    stores.read(),
    apis.read(),
    timestamp.read()
  ]).then(function (data) {
    return {
      stores: data[0],
      apis: data[1],
      timestamp: data[2]
    };
  });
}

/**
 * Expose the init command and public storage access for init things.
 */
module.exports = {
  command: init,
  data: initData
};
<|endoftext|>