<commit_before>const log = require('./logger');
const icon = require('./icon');
const pjson = require('../package.json');
const store = require('./store');
const electron = require('electron');
const AutoLaunch = require('auto-launch');
const contribution = require('contribution');

const { app, BrowserWindow, Tray, Menu, shell, ipcMain } = electron;

app.on('ready', () => {
  const streakerAutoLauncher = new AutoLaunch({
    name: pjson.name,
    path: `/Applications/${pjson.name}.app`
  });

  const tray = new Tray(icon.done);
  let usernameWindow = null;

  function createUsernameWindow() {
    if (usernameWindow) {
      usernameWindow.focus();
      return;
    }
    usernameWindow = new BrowserWindow({
      title: `${pjson.name} - Set GitHub Username`,
      frame: false,
      width: 270,
      height: 60,
      resizable: false,
      maximizable: false,
      show: false
    });
    usernameWindow.loadURL(`file://${__dirname}/username.html`);
    usernameWindow.once('ready-to-show', () => {
      const screen = electron.screen.getDisplayNearestPoint(
        electron.screen.getCursorScreenPoint()
      );
      usernameWindow.setPosition(
        Math.floor(
          screen.bounds.x +
            screen.size.width / 2 -
            usernameWindow.getSize()[0] / 2
        ),
        Math.floor(
          screen.bounds.y +
            screen.size.height / 2 -
            usernameWindow.getSize()[1] / 2
        )
      );
      usernameWindow.show();
    });
    usernameWindow.on('closed', () => {
      usernameWindow = null;
    });
    usernameWindow.on('blur', () => {
      usernameWindow.close();
    });
  }

  function createTrayMenu(contributionCount, currentStreak) {
    const username = store.get('username') || 'username not set';
    const githubProfileUrl = `https://github.com/${username}`;
    const menuTemplate = [
      { label: username, enabled: false },
      { type: 'separator' },
      { label: `Current Streak:\t${currentStreak}`, enabled: false },
      { label: `Best Streak:\t\t${currentStreak}`, enabled: false },
      { label: `Contributions:\t${contributionCount}`, enabled: false },
      { type: 'separator' },
      { label: 'Reload', accelerator: 'Cmd+R', click: requestContributionData },
      {
        label: 'Open GitHub Profile...',
        accelerator: 'Cmd+O',
        click: () => shell.openExternal(githubProfileUrl)
      },
      { type: 'separator' },
      {
        label: 'Set GitHub Username...',
        accelerator: 'Cmd+S',
        click: createUsernameWindow
      },
      {
        label: 'Preferences',
        submenu: [
          {
            label: `Launch ${pjson.name} at login`,
            type: 'checkbox',
            checked: store.get('autoLaunch'),
            click: checkbox => {
              store.set('autoLaunch', checkbox.checked);
              if (checkbox.checked) {
                streakerAutoLauncher.enable();
              } else {
                streakerAutoLauncher.disable();
              }
              log.info(`Store updated - autoLaunch=${checkbox.checked}`);
            }
          }
        ]
      },
      { type: 'separator' },
      {
        label: `About ${pjson.name}...`,
        click: () => shell.openExternal(pjson.homepage)
      },
      {
        label: 'Feedback && Support...',
        click: () => shell.openExternal(pjson.bugs.url)
      },
      { type: 'separator' },
      {
        label: `Quit ${pjson.name}`,
        accelerator: 'Cmd+Q',
        click: () => app.quit()
      }
    ];
    return Menu.buildFromTemplate(menuTemplate);
  }

  function requestContributionData() {
    tray.setImage(icon.load);
    tray.setContextMenu(createTrayMenu('Loading...', 'Loading...'));

    const username = store.get('username');

    contribution(username)
      .then(data => {
        tray.setContextMenu(createTrayMenu(data.contributions, data.streak));
        tray.setImage(data.streak > 0 ? icon.done : icon.todo);
        log.info(
          `Request successful - username=${username} streak=${
            data.streak
          } today=${data.contribution > 0}`
        );
      })
      .catch(error => {
        tray.setContextMenu(createTrayMenu('Error', 'Error'));
        tray.setImage(icon.fail);
        log.error(
          `Request failed - username=${username}) statusCode=${
            error.statusCode
          }`
        );
      });
  }

  function setUsername(event, username) {
    usernameWindow.close();
    if (username && username !== store.get('username')) {
      store.set('username', username);
      requestContributionData();
      log.info(`Store updated - username=${username}`);
    }
  }

  app.dock.hide();
  app.on('window-all-closed', () => {});
  tray.on('right-click', requestContributionData);
  ipcMain.on('setUsername', setUsername);

  requestContributionData();
  setInterval(requestContributionData, 1000 * 60 * 15); // 15 Minutes
});
<commit_msg>Rearrange requires to make eslint happy<commit_after>const electron = require('electron');
const AutoLaunch = require('auto-launch');
const contribution = require('contribution');

const log = require('./logger');
const icon = require('./icon');
const pjson = require('../package.json');
const store = require('./store');

const { app, BrowserWindow, Tray, Menu, shell, ipcMain } = electron;

app.on('ready', () => {
  const streakerAutoLauncher = new AutoLaunch({
    name: pjson.name,
    path: `/Applications/${pjson.name}.app`
  });

  const tray = new Tray(icon.done);
  let usernameWindow = null;

  function createUsernameWindow() {
    if (usernameWindow) {
      usernameWindow.focus();
      return;
    }
    usernameWindow = new BrowserWindow({
      title: `${pjson.name} - Set GitHub Username`,
      frame: false,
      width: 270,
      height: 60,
      resizable: false,
      maximizable: false,
      show: false
    });
    usernameWindow.loadURL(`file://${__dirname}/username.html`);
    usernameWindow.once('ready-to-show', () => {
      const screen = electron.screen.getDisplayNearestPoint(
        electron.screen.getCursorScreenPoint()
      );
      usernameWindow.setPosition(
        Math.floor(
          screen.bounds.x +
            screen.size.width / 2 -
            usernameWindow.getSize()[0] / 2
        ),
        Math.floor(
          screen.bounds.y +
            screen.size.height / 2 -
            usernameWindow.getSize()[1] / 2
        )
      );
      usernameWindow.show();
    });
    usernameWindow.on('closed', () => {
      usernameWindow = null;
    });
    usernameWindow.on('blur', () => {
      usernameWindow.close();
    });
  }

  function createTrayMenu(contributionCount, currentStreak) {
    const username = store.get('username') || 'username not set';
    const githubProfileUrl = `https://github.com/${username}`;
    const menuTemplate = [
      { label: username, enabled: false },
      { type: 'separator' },
      { label: `Current Streak:\t${currentStreak}`, enabled: false },
      { label: `Best Streak:\t\t${currentStreak}`, enabled: false },
      { label: `Contributions:\t${contributionCount}`, enabled: false },
      { type: 'separator' },
      { label: 'Reload', accelerator: 'Cmd+R', click: requestContributionData },
      {
        label: 'Open GitHub Profile...',
        accelerator: 'Cmd+O',
        click: () => shell.openExternal(githubProfileUrl)
      },
      { type: 'separator' },
      {
        label: 'Set GitHub Username...',
        accelerator: 'Cmd+S',
        click: createUsernameWindow
      },
      {
        label: 'Preferences',
        submenu: [
          {
            label: `Launch ${pjson.name} at login`,
            type: 'checkbox',
            checked: store.get('autoLaunch'),
            click: checkbox => {
              store.set('autoLaunch', checkbox.checked);
              if (checkbox.checked) {
                streakerAutoLauncher.enable();
              } else {
                streakerAutoLauncher.disable();
              }
              log.info(`Store updated - autoLaunch=${checkbox.checked}`);
            }
          }
        ]
      },
      { type: 'separator' },
      {
        label: `About ${pjson.name}...`,
        click: () => shell.openExternal(pjson.homepage)
      },
      {
        label: 'Feedback && Support...',
        click: () => shell.openExternal(pjson.bugs.url)
      },
      { type: 'separator' },
      {
        label: `Quit ${pjson.name}`,
        accelerator: 'Cmd+Q',
        click: () => app.quit()
      }
    ];
    return Menu.buildFromTemplate(menuTemplate);
  }

  function requestContributionData() {
    tray.setImage(icon.load);
    tray.setContextMenu(createTrayMenu('Loading...', 'Loading...'));

    const username = store.get('username');

    contribution(username)
      .then(data => {
        tray.setContextMenu(createTrayMenu(data.contributions, data.streak));
        tray.setImage(data.streak > 0 ? icon.done : icon.todo);
        log.info(
          `Request successful - username=${username} streak=${
            data.streak
          } today=${data.contribution > 0}`
        );
      })
      .catch(error => {
        tray.setContextMenu(createTrayMenu('Error', 'Error'));
        tray.setImage(icon.fail);
        log.error(
          `Request failed - username=${username}) statusCode=${
            error.statusCode
          }`
        );
      });
  }

  function setUsername(event, username) {
    usernameWindow.close();
    if (username && username !== store.get('username')) {
      store.set('username', username);
      requestContributionData();
      log.info(`Store updated - username=${username}`);
    }
  }

  app.dock.hide();
  app.on('window-all-closed', () => {});
  tray.on('right-click', requestContributionData);
  ipcMain.on('setUsername', setUsername);

  requestContributionData();
  setInterval(requestContributionData, 1000 * 60 * 15); // 15 Minutes
});
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var mysql = require('../util/sql').mysql;
var pool = require('../util/sql').pool;

/**
 * @api {get} /users/:id Get User Info
 * @apiName GetUser
 * @apiGroup User
 *
 * @apiSuccess {Object} user user 데이터
 * @apiSuccess {String} user.id 유저 id
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "id": "123456abcdef"
 *     }
 */
router.get('/:id', function(req, res, next) {
  pool.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
    if (err) throw err;

    console.log('The solution is: ', rows[0].solution);
    res.send('respond with a resource');
  });
});

/**
 * @api {post} /users Add New User
 * @apiName PostUser
 * @apiGroup User
 *
 * @apiParam {String} type 가입 타입
 *
 * @apiSuccess {Object} user user 데이터
 * @apiSuccess {String} user.id 유저 id(쿠키에 저장할 값)
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "id": "123456abcdef"
 *     }
 */
router.post('/', function(req, res, next) {

});

module.exports = router;
<commit_msg>Update login callback<commit_after>var express = require('express');
var router = express.Router();
var mysql = require('../util/sql').mysql;
var pool = require('../util/sql').pool;

/**
 * @api {get} /users/:id Get User Info
 * @apiName GetUser
 * @apiGroup User
 *
 * @apiSuccess {Object} user user 데이터
 * @apiSuccess {String} user.id 유저 id
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "id": "123456abcdef"
 *     }
 */
router.get('/:id', function(req, res, next) {
  pool.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
    if (err) throw err;

    console.log('The solution is: ', rows[0].solution);
    res.send('respond with a resource');
  });
});

router.get('/login/callback', function(req, res, next) {
  res.send(req.query);
});

/**
 * @api {post} /users Add New User
 * @apiName PostUser
 * @apiGroup User
 *
 * @apiParam {String} type 가입 타입
 *
 * @apiSuccess {Object} user user 데이터
 * @apiSuccess {String} user.id 유저 id(쿠키에 저장할 값)
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "id": "123456abcdef"
 *     }
 */
router.post('/', function(req, res, next) {

});

module.exports = router;
<|endoftext|>


<commit_before>/**
 * app.js
 */
(function (spiderOakApp, window, undefined) {
  "use strict";
  var console = window.console || {};
  console.log = console.log || function(){};
  var Backbone    = window.Backbone,
      _           = window._,
      $           = window.$;

  // Fix for lack of detach in Zepto...
  $.fn.detach = $.fn.remove;

  // Considering changing the template settings as the ERB defaults are annoying
  // _.templateSettings = {
  //   evaluate:    /\{\{#([\s\S]+?)=\}\}/g,          // {{# console.log("blah") }}
  //   interpolate: /\{\{[^#\{]([\s\S]+?)[^\}]\}\}/g, // {{ title }}
  //   escape:      /\{\{\{([\s\S]+?)\}\}\}/g         // {{{ title }}}
  // };

  _.extend(spiderOakApp, {
    initialize: function() {
      // Start listening for important app-level events
      document.addEventListener(
        "deviceready",
        this.onDeviceReady,
        false
      );
      document.addEventListener(
        "loginSuccess",
        this.onLoginSuccess,
        false
      );
      // Hax for Android 2.x not groking :active
      $(document).on("touchstart", "a", function(event) {
        var $this = $(this);
        $this.addClass("active");
      });
      $(document).on("touchend", "a", function(event) {
        var $this = $(this);
        $this.removeClass("active");
      });
      $(document).on("touchmove", "a", function(event) {
        var $this = $(this);
        $this.removeClass("active");
      });
      $(".splash").hide();
    },
    backDisabled: true,
    onDeviceReady: function() {
      document.addEventListener(
        "backbutton",
        spiderOakApp.onBackKeyDown,
        false
      );
      document.addEventListener(
        "menubutton",
        spiderOakApp.onMenuKeyDown,
        false
      );
      // @FIXME: This seems cludgey
      if (window.cssLoaded) navigator.splashscreen.hide();
      // @TODO: Instantiate any plugins
      spiderOakApp.fileViewer = window.cordova &&
        window.cordova.require("cordova/plugin/fileviewerplugin");
    },
    onLoginSuccess: function() {
      // Instantiate the menusheet and bind the spiderOakApp.accountModel
      // @TODO: refactor so menuSheetView can also be used without active login.
      spiderOakApp.menuSheetView = new spiderOakApp.MenuSheetView({
        model: spiderOakApp.accountModel
      }).render();
      // Instantiate the favorites and populate from localStorage
      var favorites = window.store.get(
        "favorites-" + spiderOakApp.accountModel.get("b32username")
      );
      favorites = favorites || [];
      spiderOakApp.favoritesCollection =
        new spiderOakApp.FavoritesCollection(favorites);
      // console.log(JSON.stringify(favorites));
    },
    onBackKeyDown: function(event) {
      event.preventDefault();
      if ($(".modal").is(":visible")) {
        return;
      }
      // @FIXME: Extend this logic a bit... it's a bit simplistic
      if ($("#main").hasClass("open")) {
        spiderOakApp.mainView.closeMenu();
        return;
      }
      if ($(".nav .back-btn").is(":visible")) {
        spiderOakApp.navigator.popView(spiderOakApp.defaultEffect);
        return;
      }
      navigator.app.exitApp();
    },
    onMenuKeyDown: function(event) {
      if ($("#main").hasClass("open")) {
        spiderOakApp.mainView.closeMenu();
      }
      else {
        spiderOakApp.mainView.openMenu();
      }
    },
    downloader: new window.FileDownloadHelper(),
    navigator: new window.BackStack.StackNavigator({el:'#subviews'}),
    noEffect: new window.BackStack.NoEffect(),
    fadeEffect: new window.BackStack.FadeEffect(),
    defaultEffect: (($.os.android) ? new window.BackStack.NoEffect() : null)
  });

/*
 * How a model in our framework determines its' composite URL.
 *
 * The URL is a concatenation of model and collection url elements.  Any
 * prevailing elements that are functions are evaluated for their result.
 *
 * - A head part is taken in this order of precedence:
 *   - the model's collection's urlBase attribute, or
 *   - the model's collection's url attribute,
 * - The url part is taken from the model's .get("url").
 *
 * @this{model}
 */
Backbone.Model.prototype.composedUrl = function() {
  var urlTail = this.get("url");
  var collection = this.collection;
  var urlHead = collection ? (collection.urlBase || collection.url) : "";
  if (typeof urlTail === "function") {
    urlTail = urlTail.call(this);
  }
  if (typeof urlHead === "function") {
    urlHead = urlHead.call(this.collection);
  }
  return urlHead + urlTail;
};
/**
 * Method to fetch model.url string and function versions identically.
 */
Backbone.Model.prototype.urlResult = function() {
  return (typeof this.url === "function") ? this.url() : this.url;
};

})(window.spiderOakApp = window.spiderOakApp || {}, window);

/* Function.bind polyfill */

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - " +
        "what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        FNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof FNOP && oThis ? this : oThis,
                          aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    FNOP.prototype = this.prototype;
    fBound.prototype = new FNOP();

    return fBound;
  };
}
<commit_msg>Travis debugging<commit_after>/**
 * app.js
 */
(function (spiderOakApp, window, undefined) {
  "use strict";
  var console = window.console || {};
  console.log = console.log || function(){};
  var Backbone    = window.Backbone,
      _           = window._,
      $           = window.$;

  // Fix for lack of detach in Zepto...
  $.fn.detach = $.fn.remove;

  // Considering changing the template settings as the ERB defaults are annoying
  // _.templateSettings = {
  //   evaluate:    /\{\{#([\s\S]+?)=\}\}/g,          // {{# console.log("blah") }}
  //   interpolate: /\{\{[^#\{]([\s\S]+?)[^\}]\}\}/g, // {{ title }}
  //   escape:      /\{\{\{([\s\S]+?)\}\}\}/g         // {{{ title }}}
  // };

  _.extend(spiderOakApp, {
    initialize: function() {
      // Start listening for important app-level events
      document.addEventListener(
        "deviceready",
        this.onDeviceReady,
        false
      );
      document.addEventListener(
        "loginSuccess",
        this.onLoginSuccess,
        false
      );
      // Hax for Android 2.x not groking :active
      $(document).on("touchstart", "a", function(event) {
        var $this = $(this);
        $this.addClass("active");
      });
      $(document).on("touchend", "a", function(event) {
        var $this = $(this);
        $this.removeClass("active");
      });
      $(document).on("touchmove", "a", function(event) {
        var $this = $(this);
        $this.removeClass("active");
      });
      $(".splash").hide();
    },
    backDisabled: true,
    onDeviceReady: function() {
      document.addEventListener(
        "backbutton",
        spiderOakApp.onBackKeyDown,
        false
      );
      document.addEventListener(
        "menubutton",
        spiderOakApp.onMenuKeyDown,
        false
      );
      // @FIXME: This seems cludgey
      if (window.cssLoaded) navigator.splashscreen.hide();
      // @TODO: Instantiate any plugins
      spiderOakApp.fileViewer = window.cordova &&
        window.cordova.require("cordova/plugin/fileviewerplugin");
    },
    onLoginSuccess: function() {
      console.log("LOGIN SUCCESS CALLED");
      // Instantiate the menusheet and bind the spiderOakApp.accountModel
      // @TODO: refactor so menuSheetView can also be used without active login.
      spiderOakApp.menuSheetView = new spiderOakApp.MenuSheetView({
        model: spiderOakApp.accountModel
      }).render();
      // Instantiate the favorites and populate from localStorage
      var favorites = window.store.get(
        "favorites-" + spiderOakApp.accountModel.get("b32username")
      );
      favorites = favorites || [];
      spiderOakApp.favoritesCollection =
        new spiderOakApp.FavoritesCollection(favorites);
      // console.log(JSON.stringify(favorites));
    },
    onBackKeyDown: function(event) {
      event.preventDefault();
      if ($(".modal").is(":visible")) {
        return;
      }
      // @FIXME: Extend this logic a bit... it's a bit simplistic
      if ($("#main").hasClass("open")) {
        spiderOakApp.mainView.closeMenu();
        return;
      }
      if ($(".nav .back-btn").is(":visible")) {
        spiderOakApp.navigator.popView(spiderOakApp.defaultEffect);
        return;
      }
      navigator.app.exitApp();
    },
    onMenuKeyDown: function(event) {
      if ($("#main").hasClass("open")) {
        spiderOakApp.mainView.closeMenu();
      }
      else {
        spiderOakApp.mainView.openMenu();
      }
    },
    downloader: new window.FileDownloadHelper(),
    navigator: new window.BackStack.StackNavigator({el:'#subviews'}),
    noEffect: new window.BackStack.NoEffect(),
    fadeEffect: new window.BackStack.FadeEffect(),
    defaultEffect: (($.os.android) ? new window.BackStack.NoEffect() : null)
  });

/*
 * How a model in our framework determines its' composite URL.
 *
 * The URL is a concatenation of model and collection url elements.  Any
 * prevailing elements that are functions are evaluated for their result.
 *
 * - A head part is taken in this order of precedence:
 *   - the model's collection's urlBase attribute, or
 *   - the model's collection's url attribute,
 * - The url part is taken from the model's .get("url").
 *
 * @this{model}
 */
Backbone.Model.prototype.composedUrl = function() {
  var urlTail = this.get("url");
  var collection = this.collection;
  var urlHead = collection ? (collection.urlBase || collection.url) : "";
  if (typeof urlTail === "function") {
    urlTail = urlTail.call(this);
  }
  if (typeof urlHead === "function") {
    urlHead = urlHead.call(this.collection);
  }
  return urlHead + urlTail;
};
/**
 * Method to fetch model.url string and function versions identically.
 */
Backbone.Model.prototype.urlResult = function() {
  return (typeof this.url === "function") ? this.url() : this.url;
};

})(window.spiderOakApp = window.spiderOakApp || {}, window);

/* Function.bind polyfill */

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - " +
        "what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        FNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof FNOP && oThis ? this : oThis,
                          aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    FNOP.prototype = this.prototype;
    fBound.prototype = new FNOP();

    return fBound;
  };
}
<|endoftext|>


<commit_before>define(function (require) {

    console.log('main');

    var $ = require('jquery'),
        lib = require('./lib'),
        controller = require('./controller/controller'),
        model = require('./model/model'),
		modelController = require('./modelController/modelController'),
		modelService = require('./modelService/modelService'),
		serviceBus = require('./serviceBus/serviceBus'),
		view = require('./view/view'),
		viewController = require('./viewController/viewController'),
		viewService = require('./viewService/viewService'),
        backbone = require('backbone'),
        underscore = require('underscore'),
        //lodash = require('lodash'), //WAS require('./../../../bower_components/lodash/dist/lodash'),
		postal = require('postal');
        //postaldiags = require('postal.diagnostics'), //WAS require('./../../../bower_components/postal.diagnostics/lib/postal.diagnostics.min'),
        //conduit = require('conduitjs'); //WAS require('./../../../bower_components/conduitjs/lib/conduit.min');

    //A fabricated API to show interaction of
    //common and specific pieces.
    controller.setModel(model);
    $(function () {
        controller.render(lib.getBody());

        //Display backbone and underscore versions
        $('body')
            .append('
backbone version: ' + backbone.VERSION + '
')
            .append('
underscore version: ' + underscore.VERSION + '
')
			.append('
Example 1 - The World\'s Simplest Subscription

');
    });
});
<commit_msg>Updated<commit_after>define(function (require) {
    console.log('main called');
    var $ = require('jquery'),
        lib = require('./lib'),
        controller = require('./controller/controller'),
        model = require('./model/model'),
		modelController = require('./modelController/modelController'),
		modelService = require('./modelService/modelService'),
		serviceBus = require('./serviceBus/serviceBus'),
		view = require('./view/view'),
		viewController = require('./viewController/viewController'),
		viewService = require('./viewService/viewService'),
        backbone = require('backbone'),
        underscore = require('underscore'),
        //lodash = require('lodash'), //WAS require('./../../../bower_components/lodash/dist/lodash'),
		postal = require('postal');
        //postaldiags = require('postal.diagnostics'), //WAS require('./../../../bower_components/postal.diagnostics/lib/postal.diagnostics.min'),
        //conduit = require('conduitjs'); //WAS require('./../../../bower_components/conduitjs/lib/conduit.min');

    //A fabricated API to show interaction of
    //common and specific pieces.
    console.log('model:');
    console.log(model);
    controller.setModel(model);
    $(function () {
        controller.render(lib.getBody());

        //Display backbone and underscore versions
        $('body')
            .append('
backbone version: ' + backbone.VERSION + '
')
            .append('
underscore version: ' + underscore.VERSION + '
')
			.append('
Example 1 - The World\'s Simplest Subscription

');
    });
});
<|endoftext|>


<commit_before>var React = require('react');
var Results = require('./components/results');
// var SearchOptions = require('./components/search-options');
var Map = require('./components/map');
var Graph = require('./components/graph');
var d3 = require('d3');
var Cortex = require('cortexjs');

var disableHashChange = false;

var defaultRoute = {
  geojson: {
    type: 'FeatureCollection',
    features: null
  },
  hidden: false
};

module.exports = React.createClass({

  getInitialState: function() {
    return {
      sidebarWidth: 450,
      route: new Cortex(defaultRoute, function(updatedRoute) {
        this.setState({
          route: updatedRoute
        });
      }.bind(this))
    };
  },

  render: function() {
    var logo;
    var inputStyle = {
       width: '100%'
    };

    if (this.props.config.viewer.logo) {
      logo = ();
      inputStyle = {
        width: 'calc(100% - 52px)'
      }
    }

    return (

              {logo}
              <input style={inputStyle} type='search' placeholder={this.props.language.searchPlaceholder}
                  onKeyDown={this.search} ref='searchInput' />

            <Results config={this.props.config} language={this.props.language} route={this.state.route} ref='results' map={this.refs.map} />

        <Map route={this.state.route} sidebarWidth={this.state.sidebarWidth} ref='map' />
        <Graph toggleLeafs={this.toggleLeafs} showLeafs={this.state.showLeafs} route={this.state.route} ref='graph' />

    );
  },

  toggleLeafs: function(){
    this.setState({showLeafs: !this.state.showLeafs });
  },

  componentDidMount: function() {
    React.findDOMNode(this.refs.searchInput).focus();

    window.onhashchange = window.onhashchange || function() {
      if (!disableHashChange) {
        this.handleHash(this.parseHash(location.hash.substring(1)));
      }
    }.bind(this);

    if (location.hash) {
      this.handleHash(this.parseHash(location.hash.substring(1)));
    }

    this.setState({
      sidebarWidth: React.findDOMNode(this.refs.container).offsetWidth
    });
  },

  search: function(event) {
    if (event.keyCode == 13) {
      // TODO: Use something like React Router for hash routing?
      // TODO: add route for selection of single concept (and even PIT?)

      var value = React.findDOMNode(this.refs.searchInput).value;

      if (value) {
        var query = this.stringToQuery(value);
      } else {
        query = {};
      }

      this.callApi(this.queryToString(query));
      this.setHash('search=' + value);
    }
  },

  queryToString: function(query) {
    var parts = [];
    Object.keys(query).forEach(function(param) {
      parts.push(param + '=' + query[param].join(','));
    });
    return parts.join('&');
  },

  stringToQuery: function(str) {
    searchTypes = [
      'q',
      'id',
      'uri',
      'name'
    ];

    var search = [];
    var params = {};
    str.match(/(?:[^\s"]+|"[^"]*")+/g)
      .map(function(part) {
        return part.trim();
      })
      .filter(function(part) {
        return part;
      })
      .forEach(function(part) {
        if (part.indexOf('=') > -1) {
          var paramValue = part.split('=');
          var param = paramValue[0].trim();
          var value = paramValue[1].trim();

          if (searchTypes.indexOf(param) > -1) {
            search.push(value);
          } else if (param) {
            if (!params[param]) {
              params[param] = [];
            }
            params[param].push(value);
          }
        } else {
          search.push(part);
        }
      });

    if (!search.length) {
      search = ['*'];
    }

    params.q = search;

    return params;
  },

  callApi: function(query) {
    if (!query) {
      this.state.route.set(defaultRoute);
      return;
    }

    d3.json(this.getApiUrl(query), function(error, geojson) {
      var errorMessage;
      if (error) {
        try {
          errorMessage = JSON.parse(error.response).error;
        } catch (e) {
          errorMessage = this.props.language.invalidResponse;
        }
      }
      // TODO: use errorMessage

      var route = {
        geojson: geojson,
        error: errorMessage,
        fitBounds: true,
        search: query,
        hidden: false,
        graph: false,
        concept: {
          highlighted: -1,
          selected: geojson.features.length === 1 ? 0 : -1
        },
        pit: {
          highlighted: -1
        }
      };
      this.state.route.set(route);
    }.bind(this));
  },

  componentDidUpdate: function() {
    var fitBounds = this.state.route.fitBounds && this.state.route.fitBounds.getValue();
    var features = this.state.route.geojson && this.state.route.geojson.getValue().features;
    if (fitBounds && features.length) {
      this.refs.map.fitBounds(this.refs.map.getConceptLayer());
      this.state.route.fitBounds.set(false);
    }
  },

  getApiUrl: function(queryString) {
    return this.props.config.api.baseUrl + 'search?' + queryString;
  },

  parseHash: function (hash) {
    // TODO: move to/merge with queryToString function
    var params = {};
    decodeURIComponent(hash).split('&').forEach(function(param) {
      if (param.indexOf('=') > -1) {
        var kv = param.split('=');
        params[kv[0]] = kv.slice(1).join('=');
      }
    });

    return params;
  },

  handleHash: function (params){
    if (params.search) {
      React.findDOMNode(this.refs.searchInput).value = params.search;
      this.search({keyCode: 13});
    }
  },

  setHash: function(hash){
    disableHashChange = true;
    location.hash = hash;
    setTimeout(function() {
      disableHashChange = false;
    }, 1000);
  }

});
<commit_msg>encode hash uri<commit_after>var React = require('react');
var Results = require('./components/results');
// var SearchOptions = require('./components/search-options');
var Map = require('./components/map');
var Graph = require('./components/graph');
var d3 = require('d3');
var Cortex = require('cortexjs');

var disableHashChange = false;

var defaultRoute = {
  geojson: {
    type: 'FeatureCollection',
    features: null
  },
  hidden: false
};

module.exports = React.createClass({

  getInitialState: function() {
    return {
      sidebarWidth: 450,
      route: new Cortex(defaultRoute, function(updatedRoute) {
        this.setState({
          route: updatedRoute
        });
      }.bind(this))
    };
  },

  render: function() {
    var logo;
    var inputStyle = {
       width: '100%'
    };

    if (this.props.config.viewer.logo) {
      logo = ();
      inputStyle = {
        width: 'calc(100% - 52px)'
      }
    }

    return (

              {logo}
              <input style={inputStyle} type='search' placeholder={this.props.language.searchPlaceholder}
                  onKeyDown={this.search} ref='searchInput' />

            <Results config={this.props.config} language={this.props.language} route={this.state.route} ref='results' map={this.refs.map} />

        <Map route={this.state.route} sidebarWidth={this.state.sidebarWidth} ref='map' />
        <Graph toggleLeafs={this.toggleLeafs} showLeafs={this.state.showLeafs} route={this.state.route} ref='graph' />

    );
  },

  toggleLeafs: function(){
    this.setState({showLeafs: !this.state.showLeafs });
  },

  componentDidMount: function() {
    React.findDOMNode(this.refs.searchInput).focus();

    window.onhashchange = window.onhashchange || function() {
      if (!disableHashChange) {
        this.handleHash(this.parseHash(location.hash.substring(1)));
      }
    }.bind(this);

    if (location.hash) {
      this.handleHash(this.parseHash(location.hash.substring(1)));
    }

    this.setState({
      sidebarWidth: React.findDOMNode(this.refs.container).offsetWidth
    });
  },

  search: function(event) {
    if (event.keyCode == 13) {
      // TODO: Use something like React Router for hash routing?
      // TODO: add route for selection of single concept (and even PIT?)

      var value = React.findDOMNode(this.refs.searchInput).value;

      if (value) {
        var query = this.stringToQuery(value);
      } else {
        query = {};
      }

      this.callApi(this.queryToString(query));
      this.setHash('search=' + encodeURIComponent(value));
    }
  },

  queryToString: function(query) {
    var parts = [];
    Object.keys(query).forEach(function(param) {
      parts.push(param + '=' + query[param].join(','));
    });
    return parts.join('&');
  },

  stringToQuery: function(str) {
    searchTypes = [
      'q',
      'id',
      'uri',
      'name'
    ];

    var search = [];
    var params = {};
    str.match(/(?:[^\s"]+|"[^"]*")+/g)
      .map(function(part) {
        return part.trim();
      })
      .filter(function(part) {
        return part;
      })
      .forEach(function(part) {
        if (part.indexOf('=') > -1) {
          var paramValue = part.split('=');
          var param = paramValue[0].trim();
          var value = paramValue[1].trim();

          if (searchTypes.indexOf(param) > -1) {
            search.push(value);
          } else if (param) {
            if (!params[param]) {
              params[param] = [];
            }
            params[param].push(value);
          }
        } else {
          search.push(part);
        }
      });

    if (!search.length) {
      search = ['*'];
    }

    params.q = search;

    return params;
  },

  callApi: function(query) {
    if (!query) {
      this.state.route.set(defaultRoute);
      return;
    }

    d3.json(this.getApiUrl(query), function(error, geojson) {
      var errorMessage;
      if (error) {
        try {
          errorMessage = JSON.parse(error.response).error;
        } catch (e) {
          errorMessage = this.props.language.invalidResponse;
        }
      }
      // TODO: use errorMessage

      var route = {
        geojson: geojson,
        error: errorMessage,
        fitBounds: true,
        search: query,
        hidden: false,
        graph: false,
        concept: {
          highlighted: -1,
          selected: geojson.features.length === 1 ? 0 : -1
        },
        pit: {
          highlighted: -1
        }
      };
      this.state.route.set(route);
    }.bind(this));
  },

  componentDidUpdate: function() {
    var fitBounds = this.state.route.fitBounds && this.state.route.fitBounds.getValue();
    var features = this.state.route.geojson && this.state.route.geojson.getValue().features;
    if (fitBounds && features.length) {
      this.refs.map.fitBounds(this.refs.map.getConceptLayer());
      this.state.route.fitBounds.set(false);
    }
  },

  getApiUrl: function(queryString) {
    return this.props.config.api.baseUrl + 'search?' + queryString;
  },

  parseHash: function (hash) {
    // TODO: move to/merge with queryToString function
    var params = {};
    decodeURIComponent(hash).split('&').forEach(function(param) {
      if (param.indexOf('=') > -1) {
        var kv = param.split('=');
        params[kv[0]] = kv.slice(1).join('=');
      }
    });

    return params;
  },

  handleHash: function (params){
    if (params.search) {
      React.findDOMNode(this.refs.searchInput).value = params.search;
      this.search({keyCode: 13});
    }
  },

  setHash: function(hash){
    disableHashChange = true;
    location.hash = hash;
    setTimeout(function() {
      disableHashChange = false;
    }, 1000);
  }

});
<|endoftext|>


<commit_before>var exportHtmlCtrl = function ($scope,creature,Creature,$http,$mdDialog,$mdToast) {

	var header = "";
	Creature.calculateCreatureDetails(creature);

	$scope.loading = true;

	$http.get("/assets/data/valloricHeader.html").then(function(response){
		header = response.data;
		$scope.loading = false;
		$scope.export.html = generateHTML(creature);
	});

	var generateHeading = function(creature){
		var html = '<creature-heading>\n' +
			'
'+creature.name+'
\n' +
			'
'+ creature.stats.size.charAt(0).toUpperCase() + creature.stats.size.slice(1).toLowerCase() + ' ' + creature.stats.race.toLowerCase() + ', ' + creature.stats.alignment.toLowerCase() + '
\n' +
			'</creature-heading>';
		return html;
	}

	var generateTopStats = function(creature){
		var html = '<top-stats>\n' +
			'<property-line>\n' +
			'
Armor Class
\n' +
			'
' + creature.stats.armorClass + ' ' + (creature.stats.armorType ? ('(' + creature.stats.armorType.toLowerCase() + ')') : '') + '
\n' +
			'</property-line>\n' +
			'<property-line>\n' +
			'
Hit Points
\n' +
			'
' + creature.stats.hitPointsStr + '
\n' +
			'</property-line>\n' +
			'<property-line>\n' +
			'
Speed
\n' +
			'
' + creature.stats.speed + '
\n' +
			'</property-line>\n' +
			'<abilities-block data-cha="' + creature.stats.abilityScores.charisma + '" data-con="' + creature.stats.abilityScores.constitution + '" data-dex="' + creature.stats.abilityScores.dexterity + '" data-int="' + creature.stats.abilityScores.intelligence + '" data-str="' + creature.stats.abilityScores.strength + '" data-wis="' + creature.stats.abilityScores.wisdom + '"\n';
			'</abilities-block>\n' +
			//CONTINUE FROM HERE (damage immunities, etc)
		return html;
	}

	var generateProperties = function(title,abilities){
		var html = "";

		return html;
	}

	var generateLegendaryActions = function(creature){
		var html = "";

		return html;
	}

	var generateDescription = function(creature){
		var html = "";

		return html;
	}

	var generateHTML = function(creature){
		var html = header +
			'<stat-block>\n' +
			generateHeading(creature) +
			generateTopStats(creature) +
			generateProperties('',creature.additionalAbilities) +
			generateProperties('Actions',creature.actions) +
			generateProperties('Reactions',creature.reactions) +
			generateLegendaryActions(creature) +
			generateDescription(creature) +
			'</statblock>\n' +
			'\n' +
			'\n';
		return(html);
	}

	$scope.export = {
		html: ''
	};

	$scope.onCopy = function(e) {
		console.log("copied!");
	}

	var clipboard = new Clipboard('#copy-to-clipboard', {
		text: function(trigger){
			return($scope.export.html);
		}
	});

	clipboard.on('success', function(e) {
		$mdToast.show(
			$mdToast.simple()
				.textContent("HTML copied to clipboard!")
				.position("bottom right")
				.parent(document.getElementById('export-dialog'))
				.hideDelay(3000)
		);
		e.clearSelection();
	});

	clipboard.on('error', function(e) {
		$mdToast.show(
			$mdToast.simple()
				.textContent("Press CTRL-C to copy HTML!")
				.position("bottom right")
				.parent(document.getElementById('export-dialog'))
				.hideDelay(3000)
		);
		e.clearSelection();
	});

	$scope.cancel = function() {
    $mdDialog.cancel();
  };

};

angular.module('myApp').controller('exportHtmlCtrl',exportHtmlCtrl);<commit_msg>working on exporting creature stat block to html<commit_after>var exportHtmlCtrl = function ($scope,creature,Creature,$http,$mdDialog,$mdToast) {

	var header = "";
	Creature.calculateCreatureDetails(creature);

	$scope.loading = true;

	$http.get("/assets/data/valloricHeader.html").then(function(response){
		header = response.data;
		$scope.loading = false;
		$scope.export.html = generateHTML(creature);
	});

	var generateHeading = function(creature){
		var html = '<creature-heading>\n' +
			'
'+creature.name+'
\n' +
			'
'+ creature.stats.size.charAt(0).toUpperCase() + creature.stats.size.slice(1).toLowerCase() + ' ' + creature.stats.race.toLowerCase() + ', ' + creature.stats.alignment.toLowerCase() + '
\n' +
			'</creature-heading>';
		return html;
	}

	var generateTopStats = function(creature){
		var html = '<top-stats>\n' +
			'<property-line>\n' +
			'
Armor Class
\n' +
			'
' + creature.stats.armorClass + ' ' + (creature.stats.armorType ? ('(' + creature.stats.armorType.toLowerCase() + ')') : '') + '
\n' +
			'</property-line>\n' +
			'<property-line>\n' +
			'
Hit Points
\n' +
			'
' + creature.stats.hitPointsStr + '
\n' +
			'</property-line>\n' +
			'<property-line>\n' +
			'
Speed
\n' +
			'
' + creature.stats.speed + '
\n' +
			'</property-line>\n' +
			'<abilities-block data-cha="' + creature.stats.abilityScores.charisma + '" data-con="' + creature.stats.abilityScores.constitution + '" data-dex="' + creature.stats.abilityScores.dexterity + '" data-int="' + creature.stats.abilityScores.intelligence + '" data-str="' + creature.stats.abilityScores.strength + '" data-wis="' + creature.stats.abilityScores.wisdom + '">\n' +
			'</abilities-block>\n';
		if(creature.stats.savingThrows.length>0){
			html = html +
				'<property-line>\n' +
				'
Saving Throws
\n' +
				'
';
			for(var i=0;i<creature.stats.savingThrows.length;i++){
				if(i>0)
					html = html + ', ';
				html = html + creature.stats.savingThrows[i].modifierStr;
			}
			html = html +
				'
\n' +
				'</property-line>\n';
		}
		if(creature.stats.skills.length>0){
			html = html +
				'<property-line>\n' +
				'
Skills
\n' +
				'
';
			for(var i=0;i<creature.stats.skills.length;i++){
				if(i>0)
					html = html + ', ';
				html = html + creature.stats.skills[i].modifierStr;
			}
			html = html +
				'
\n' +
				'</property-line>\n';
		}
		if(creature.stats.damageVulnerabilities.length>0){
			html = html +
				'<property-line>\n' +
				'
Damage Vulnerabilities
\n' +
				'
';
			for(var i=0;i<creature.stats.damageVulnerabilities.length;i++){
				if(i>0)
					html = html + ', ';
				html = html + creature.stats.damageVulnerabilities[i].toLowerCase();
			}
			html = html +
				'
\n' +
				'</property-line>\n';
		}
		if(creature.stats.damageResistances.length>0){
			html = html +
				'<property-line>\n' +
				'
Damage Resistances
\n' +
				'
';
			for(var i=0;i<creature.stats.damageResistances.length;i++){
				if(i>0)
					html = html + ', ';
				html = html + creature.stats.damageResistances[i].toLowerCase();
			}
			html = html +
				'
\n' +
				'</property-line>\n';
		}
		if(creature.stats.damageImmunities.length>0){
			html = html +
				'<property-line>\n' +
				'
Damage Immunities
\n' +
				'
';
			for(var i=0;i<creature.stats.damageImmunities.length;i++){
				if(i>0)
					html = html + ', ';
				html = html + creature.stats.damageImmunities[i].toLowerCase();
			}
			html = html +
				'
\n' +
				'</property-line>\n';
		}
		if(creature.stats.conditionImmunities.length>0){
			html = html +
				'<property-line>\n' +
				'
Condition Immunities
\n' +
				'
';
			for(var i=0;i<creature.stats.conditionImmunities.length;i++){
				if(i>0)
					html = html + ', ';
				html = html + creature.stats.conditionImmunities[i].toLowerCase();
			}
			html = html +
				'
\n' +
				'</property-line>\n';
		}
		//Senses
		html = html +
			'<property-line>\n' +
			'
Senses
\n' +
			'
';
		for(var i=0;i<creature.stats.senses.length;i++){
			if(i>0)
				html = html + ', ';
			html = html + creature.stats.senses[i].toLowerCase();
		}
		if(creature.stats.senses.length>0)
			html = html + ', ';
		html = html +
			'passive Perception ' + creature.stats.passivePerception + '\n' +
			'
\n' +
			'</property-line>\n';
		//Languages
		html = html +
			'<property-line>\n' +
			'
Languages
\n' +
			'
';
		if(creature.stats.languages.length==0)
			html = html + '—';
		else {
			for(var i=0;i<creature.stats.languages.length;i++){
				if(i>0)
					html = html + ', ';
				var language = creature.stats.languages[i];
				html = html + language.charAt(0).toUpperCase() + language.slice(1).toLowerCase();
			}
		}
		html = html +
			'
\n' +
			'</property-line>\n';
		//Challenge Rating
		html = html +
			'<property-line>\n' +
			'
Challenge
\n' +
			'
' + creature.stats.challengeRatingStr + ' (' + creature.stats.experiencePoints + ' XP)\n' +
			'
\n' +
			'</property-line>\n';
		html = html +
			'</top-stats>\n';
		return html;
	}

	var generateAbility = function(ability){
		var html = '<property-block>\n' +
			'
' + ability.name + '
\n' +
			'
' + ability.description + '
\n' +
			'</property-block>\n';
		return(html);
	}

	var generateProperties = function(title,abilities){
		var html = '';
		if(abilities && abilities.length>0){
			if(title)
				html = html + '
' + title + '
\n';
			for(var i=0;i<abilities.length;i++){
				html = html + generateAbility(abilities[i]);
			}
		}
		return html;
	}

	var generateLegendaryActions = function(creature){
		var html = '';
		if(creature.stats.legendaryActions && creature.stats.legendaryActions.length>0) {
			html = html + '
Legendary Actions
\n' +
				'
The ' + creature.name.toLowerCase() + ' can take ' + creature.stats.legendaryActionsPerRound + ' legendary actions, choosing from the options below. Only one legendary action option can be used at a time and only at the end of another creature\'s turn. The ' + creature.name.toLowerCase() + ' regains spent legendary actions at the start of its turn.
\n' +
				'\n' +
				generateProperties(null,creature.stats.legendaryActions);
		}
		return html;
	}

	var generateDescription = function(creature){
		var html = '';
		if(creature.flavor.description && creature.flavor.description.length>0){
			html = html + '
Description
\n' +
				'
' + creature.flavor.descriptionHtml + '
\n';
		}
		return html;
	}

	var generateHTML = function(creature){
		var html = header +
			'<stat-block>\n' +
			generateHeading(creature) +
			generateTopStats(creature) +
			generateProperties('',creature.stats.additionalAbilities) +
			generateProperties('Actions',creature.stats.actions) +
			generateProperties('Reactions',creature.stats.reactions) +
			generateLegendaryActions(creature) +
			generateDescription(creature) +
			'</statblock>\n' +
			'\n' +
			'\n';
		return(html);
	}

	$scope.export = {
		html: ''
	};

	$scope.onCopy = function(e) {
		console.log("copied!");
	}

	var clipboard = new Clipboard('#copy-to-clipboard', {
		text: function(trigger){
			return($scope.export.html);
		}
	});

	clipboard.on('success', function(e) {
		$mdToast.show(
			$mdToast.simple()
				.textContent("HTML copied to clipboard!")
				.position("bottom right")
				.parent(document.getElementById('export-dialog'))
				.hideDelay(3000)
		);
		e.clearSelection();
	});

	clipboard.on('error', function(e) {
		$mdToast.show(
			$mdToast.simple()
				.textContent("Press CTRL-C to copy HTML!")
				.position("bottom right")
				.parent(document.getElementById('export-dialog'))
				.hideDelay(3000)
		);
		e.clearSelection();
	});

	$scope.cancel = function() {
    $mdDialog.cancel();
  };

};

angular.module('myApp').controller('exportHtmlCtrl',exportHtmlCtrl);<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var pdf = require('html-pdf');
var express = require('express');
var router = express.Router();
var User = require('../../../models/user');
var Church = require('../../../models/church');

function getChurchAndValidate (options, callback) {
  User.getUserAndValidate(options.email, options.accessToken, function (user) {
    Church
      .findOne({
        "_id": options.id
      })
      .where({
        $or: [{
          "_id": {
            $in : user.churches
          }
        }, {
          "createdBy": user._id,
        }]
      })
      .exec(function(err, result) {
        callback(result);
      });
  });
}

router.get('/:id', function (req, res) {
  var email = req.session.email;
  var accessToken = req.cookies.accessToken;
  var id = req.params.id;

  if (!email || !accessToken || !id) {
    return res.status(401).json({
      success: false,
      message: "Authentication error."
    });
  }

  getChurchAndValidate({
    email: email,
    accessToken: accessToken,
    id: id,
  }, function (church) {
    // create html generation from returned church
    var html = fs.readFileSync(path.join(__dirname,'./mailingLabels.html'), 'utf8');
    var options = {
      "format": "Letter",
      "base": "http://churchetto.com/",
      "httpHeaders": {
        "email": email,
        "token": accessToken,
      }
    };

    var members = "";
    var openRow = false;
    church.members.sort(function (a,b) {
      var nameA = "";
      var nameB = "";
      if (a.lastName) {
        nameA = a.lastName.toLowerCase();
        if (a.firstName) {
          nameA = nameA + a.firstName.toLowerCase();
        }
      }
      if (b.lastName) {
        nameB = b.lastName.toLowerCase();
        if (b.firstName) {
          nameB = nameB + b.firstName.toLowerCase();
        }
      }
      if (nameA < nameB) //sort string ascending
        return -1;
      if (nameA > nameB)
        return 1;
      return 0; //default return value (no sorting)
    }).map(function (member,i) {
      if (!member.address) { member.address = {} }
      if (!member.address.line1) { member.address.line1 = "" }
      if (!member.address.line2) { member.address.line2 = "" }
      if (!member.address.city) { member.address.city = "" }
      if (!member.address.state) { member.address.state = "" }
      if (!member.address.zip) { member.address.zip = "" }

      var memberAddress = "";
      if (member.address && member.address.line1) {
        memberAddress =
          "
"
            + member.address.line1
            + "
" + member.address.city
            + ", " + member.address.state
            + " " + member.address.zip
          + "
"
      } else {
        memberAddress = "
No Address Data Available
";
      }

      var remainder = i % 3;
      if (remainder === 0) {
        openRow = true;
        members = members
        + "
";
      }

      members = members
      + "
"
        + "
"
          + "
"
            + member.firstName
            + " " + member.lastName
          + "
"
          + memberAddress
        + "
"
      + "
";

      if (remainder === 2) {
        openRow = false;
        members = members
        + "
";
      }
    });

    if (openRow === false) {
      members = members + "
"
    }

    html = html.replace('{MEMBERS}',members);

    //res.send(html);
    pdf.create(html, options).toStream(function(err, stream){
      if (err) return console.log(err);
      stream.pipe(res);
    });
  });

});

module.exports = router;
<commit_msg>Mailing labels<commit_after>var fs = require('fs');
var path = require('path');
var pdf = require('html-pdf');
var express = require('express');
var router = express.Router();
var User = require('../../../models/user');
var Church = require('../../../models/church');

function getChurchAndValidate (options, callback) {
  User.getUserAndValidate(options.email, options.accessToken, function (user) {
    Church
      .findOne({
        "_id": options.id
      })
      .where({
        $or: [{
          "_id": {
            $in : user.churches
          }
        }, {
          "createdBy": user._id,
        }]
      })
      .exec(function(err, result) {
        callback(result);
      });
  });
}

router.get('/:id', function (req, res) {
  var email = req.session.email;
  var accessToken = req.cookies.accessToken;
  var id = req.params.id;

  if (!email || !accessToken || !id) {
    return res.status(401).json({
      success: false,
      message: "Authentication error."
    });
  }

  getChurchAndValidate({
    email: email,
    accessToken: accessToken,
    id: id,
  }, function (church) {
    // create html generation from returned church
    var html = fs.readFileSync(path.join(__dirname,'./mailingLabels.html'), 'utf8');
    var options = {
      "format": "Letter",
      "base": "http://churchetto.com/",
      "httpHeaders": {
        "email": email,
        "token": accessToken,
      }
    };

    var members = "";
    var openRow = false;
    church.members.sort(function (a,b) {
      var nameA = "";
      var nameB = "";
      if (a.lastName) {
        nameA = a.lastName.toLowerCase();
        if (a.firstName) {
          nameA = nameA + a.firstName.toLowerCase();
        }
      }
      if (b.lastName) {
        nameB = b.lastName.toLowerCase();
        if (b.firstName) {
          nameB = nameB + b.firstName.toLowerCase();
        }
      }
      if (nameA < nameB) //sort string ascending
        return -1;
      if (nameA > nameB)
        return 1;
      return 0; //default return value (no sorting)
    }).map(function (member,i) {
      if (!member.address) { member.address = {} }
      if (!member.address.line1) { member.address.line1 = "" }
      if (!member.address.line2) { member.address.line2 = "" }
      if (!member.address.city) { member.address.city = "" }
      if (!member.address.state) { member.address.state = "" }
      if (!member.address.zip) { member.address.zip = "" }

      var memberAddress = "";
      if (member.address && member.address.line1) {
        memberAddress =
          "
"
            + member.address.line1
            + " " + member.address.line2
            + "
" + member.address.city
            + ", " + member.address.state
            + " " + member.address.zip
          + "
"
      } else {
        memberAddress = "
No Address Data Available
";
      }

      var remainder = i % 3;
      if (remainder === 0) {
        openRow = true;
        members = members
        + "
";
      }

      members = members
      + "
"
        + "
"
          + "
"
            + member.firstName
            + " " + member.lastName
          + "
"
          + memberAddress
        + "
"
      + "
";

      if (remainder === 2) {
        openRow = false;
        members = members
        + "
";
      }
    });

    if (openRow === false) {
      members = members + "
"
    }

    html = html.replace('{MEMBERS}',members);

    //res.send(html);
    pdf.create(html, options).toStream(function(err, stream){
      if (err) return console.log(err);
      stream.pipe(res);
    });
  });

});

module.exports = router;
<|endoftext|>


<commit_before>/*eslint-disable*/

//can't use Backbone.Collection for checked because VirtualCollection change prototype

const CheckableBehavior = {};

function __updateChecked() {
    this.checked = {};
    this.collection.each(model => {
        if (model.checked) {
            this.checked[model.cid] = model;
        }
    });
    this.__triggerCheck();
}

CheckableBehavior.CheckableCollection = function(collection) {
    this.collection = collection;
    this.__updateChecked();
    collection.on('add remove reset update', function() {
        if (this.internalCheck || collection.internalUpdate) {
            return;
        }
        this.__debounceUpdateChecked();
    });
};

_.extend(CheckableBehavior.CheckableCollection.prototype, {
    check(model) {
        if (this.internalCheck || this.checked[model.cid]) {
            return;
        }

        this.checked[model.cid] = model;
        model.check();
        this.__triggerCheck();
    },

    uncheck(model) {
        if (this.internalCheck || !this.checked[model.cid]) {
            return;
        }

        delete this.checked[model.cid];
        model.uncheck();
        this.__triggerCheck();
    },

    checkSome(model) {
        if (!this.checked[model.cid]) {
            return;
        }

        delete this.checked[model.cid];
        model.checkSome();
        this.__triggerCheck();
    },

    checkAll() {
        this.internalCheck = true;
        this.each(model => {
            this.checked[model.cid] = model;
            model.check();
        });
        this.internalCheck = false;
        this.__triggerCheck();
    },

    uncheckAll() {
        this.internalCheck = true;
        this.each(model => {
            delete this.checked[model.cid];
            model.uncheck();
        });
        this.internalCheck = false;
        this.__triggerCheck();
    },

    toggleCheckAll() {
        const checkedLength = this.checked ? Object.keys(this.checked).length : 0;
        if (checkedLength === this.length) {
            this.uncheckAll();
        } else {
            this.checkAll();
        }
    },

    updateTreeNodesCheck(model, updateParent = true) {
        if (model.children && model.children.length) {
            model.children.forEach(child => {
                if (typeof child.check !== 'function') {
                    child.checked = Boolean(model.checked);
                } else if (model.checked) {
                    child.check();
                } else {
                    child.uncheck();
                }
                this.updateTreeNodesCheck(child, false);
            });
        }
        if (!updateParent) {
            return;
        }
        let parent = model.parentModel;
        while (parent && parent.children) {
            const length = parent.children.length;
            const checkedLength = parent.children.filter(child => child.checked).length;
            if (checkedLength === length) {
                parent.check();
            } else if (checkedLength > 0 && checkedLength < length) {
                parent.checkSome();
            } else if (checkedLength === 0) {
                parent.uncheck();
            }
            parent = parent.parentModel;
        }
    },

    __debounceUpdateChecked: _.debounce(__updateChecked, 10),

    __updateChecked,

    __triggerCheck() {
        const checkedLength = this.checked ? Object.keys(this.checked).length : 0;
        const length = this.length;

        if (checkedLength !== this.prevChechLength) {
            this.prevChechLength = checkedLength;

            if (checkedLength === length) {
                this.collection.trigger('check:all', this, 'checked');
                return;
            }

            if (checkedLength === 0) {
                this.collection.trigger('check:none', this, 'unchecked');
                return;
            }

            if (checkedLength > 0 && checkedLength < length) {
                this.collection.trigger('check:some', this, 'checkedSome');
            }
        }
    }
});

CheckableBehavior.CheckableModel = function(model) {
    this.model = model;
};

_.extend(CheckableBehavior.CheckableModel.prototype, {
    check() {
        if (this.checked) {
            return;
        }

        this.checked = true;
        this.trigger('checked', this);

        if (this.collection) {
            this.collection.check(this);
        }
    },

    uncheck() {
        if (this.checked === false) {
            return;
        }

        this.checked = false;
        this.trigger('unchecked', this);

        if (this.collection) {
            this.collection.uncheck(this);
        }
    },

    checkSome() {
        if (this.checked === null) {
            return;
        }

        this.checked = null;
        this.trigger('checked:some', this);
        if (this.collection) {
            this.collection.checkSome(this);
        }
    },

    toggleChecked() {
        if (this.checked) {
            this.uncheck();
        } else {
            this.check();
        }
    }
});

export default CheckableBehavior;
export const CheckableCollection = CheckableBehavior.CheckableCollection;
export const CheckableModel = CheckableBehavior.CheckableModel;
<commit_msg>Checkable behavior fix<commit_after>/*eslint-disable*/

//can't use Backbone.Collection for checked because VirtualCollection change prototype

const CheckableBehavior = {};

CheckableBehavior.CheckableCollection = function(collection) {
    this.collection = collection;
    this.__updateChecked();
    this.__debounceUpdateChecked = _.debounce(this.__updateChecked, 10);
    collection.on('add remove reset update', function() {
        if (this.internalCheck || collection.internalUpdate) {
            return;
        }
        this.__debounceUpdateChecked();
    });
};

_.extend(CheckableBehavior.CheckableCollection.prototype, {
    check(model) {
        if (this.internalCheck || this.checked[model.cid]) {
            return;
        }

        this.checked[model.cid] = model;
        model.check();
        this.__triggerCheck();
    },

    uncheck(model) {
        if (this.internalCheck || !this.checked[model.cid]) {
            return;
        }

        delete this.checked[model.cid];
        model.uncheck();
        this.__triggerCheck();
    },

    checkSome(model) {
        if (!this.checked[model.cid]) {
            return;
        }

        delete this.checked[model.cid];
        model.checkSome();
        this.__triggerCheck();
    },

    checkAll() {
        this.internalCheck = true;
        this.each(model => {
            this.checked[model.cid] = model;
            model.check();
        });
        this.internalCheck = false;
        this.__triggerCheck();
    },

    uncheckAll() {
        this.internalCheck = true;
        this.each(model => {
            delete this.checked[model.cid];
            model.uncheck();
        });
        this.internalCheck = false;
        this.__triggerCheck();
    },

    toggleCheckAll() {
        const checkedLength = this.checked ? Object.keys(this.checked).length : 0;
        if (checkedLength === this.length) {
            this.uncheckAll();
        } else {
            this.checkAll();
        }
    },

    updateTreeNodesCheck(model, updateParent = true) {
        if (model.children && model.children.length) {
            model.children.forEach(child => {
                if (typeof child.check !== 'function') {
                    child.checked = Boolean(model.checked);
                } else if (model.checked) {
                    child.check();
                } else {
                    child.uncheck();
                }
                this.updateTreeNodesCheck(child, false);
            });
        }
        if (!updateParent) {
            return;
        }
        let parent = model.parentModel;
        while (parent && parent.children) {
            const length = parent.children.length;
            const checkedLength = parent.children.filter(child => child.checked).length;
            if (checkedLength === length) {
                parent.check();
            } else if (checkedLength > 0 && checkedLength < length) {
                parent.checkSome();
            } else if (checkedLength === 0) {
                parent.uncheck();
            }
            parent = parent.parentModel;
        }
    },

    __updateChecked() {
        this.checked = {};
        this.collection.each(model => {
            if (model.checked) {
                this.checked[model.cid] = model;
            }
        });
        this.__triggerCheck();
    },

    __triggerCheck() {
        const checkedLength = this.checked ? Object.keys(this.checked).length : 0;
        const length = this.length;

        if (checkedLength !== this.prevChechLength) {
            this.prevChechLength = checkedLength;

            if (checkedLength === length) {
                this.collection.trigger('check:all', this, 'checked');
                return;
            }

            if (checkedLength === 0) {
                this.collection.trigger('check:none', this, 'unchecked');
                return;
            }

            if (checkedLength > 0 && checkedLength < length) {
                this.collection.trigger('check:some', this, 'checkedSome');
            }
        }
    }
});

CheckableBehavior.CheckableModel = function(model) {
    this.model = model;
};

_.extend(CheckableBehavior.CheckableModel.prototype, {
    check() {
        if (this.checked) {
            return;
        }

        this.checked = true;
        this.trigger('checked', this);

        if (this.collection) {
            this.collection.check(this);
        }
    },

    uncheck() {
        if (this.checked === false) {
            return;
        }

        this.checked = false;
        this.trigger('unchecked', this);

        if (this.collection) {
            this.collection.uncheck(this);
        }
    },

    checkSome() {
        if (this.checked === null) {
            return;
        }

        this.checked = null;
        this.trigger('checked:some', this);
        if (this.collection) {
            this.collection.checkSome(this);
        }
    },

    toggleChecked() {
        if (this.checked) {
            this.uncheck();
        } else {
            this.check();
        }
    }
});

export default CheckableBehavior;
export const CheckableCollection = CheckableBehavior.CheckableCollection;
export const CheckableModel = CheckableBehavior.CheckableModel;
<|endoftext|>


<commit_before>const FileReader = require('../infrastructure/external_services/file-reader');
const DropboxClient = require('../infrastructure/external_services/dropbox-client');
const mailJet = require('../infrastructure/mailing/mailjet');
const config = require('../infrastructure/config');
const { isEmpty } = require('lodash');
const articleRepository = require('../domain/repositories/article-repository');
const chapterRepository = require('../domain/repositories/chapter-repository');
const subscriptionRepository = require('../domain/repositories/subscription-repository');
const articlesChangedEmailTemplate = require('../infrastructure/mailing/articles-changed-email-template');

function serializeArticles(metadatas) {
  return metadatas
    .map(metadata => ({
      dropboxId: metadata.name,
      imgPath: `/${metadata.name}/img0.jpg`,
    }));
}

function _compareDropboxAndDatabaseArticles(freshArticles) {
  return articleRepository.getAll()
    .then((oldArticles) => {
      const addedArticles = freshArticles.reduce((accumulatedArticles, freshArticle) => {
        const matchedArticles = oldArticles.filter(({ dropboxId }) => dropboxId === freshArticle.dropboxId);
        if (matchedArticles.length === 0) {
          accumulatedArticles.push(freshArticle);
        }
        return accumulatedArticles;
      }, []);
      const hasChanges = !isEmpty(addedArticles);
      return Promise.resolve({ addedArticles, hasChanges });
    });
}

function _transformToDownloadableLink(response) {
  if (isEmpty(response)) {
    return '';
  }
  return response.url.replace(/.$/, '1');
}

function _shareImg(article) {
  return DropboxClient.createSharedLink(article.imgPath)
    .then(imgLink => ({
      dropboxId: article.dropboxId,
      imgLink: _transformToDownloadableLink(imgLink),
    }));
}

function shareImages(articles) {
  const articlesWithAll = articles.reduce((promises, article) => {
    const articleWithAll = _shareImg(article);
    promises.push(articleWithAll);
    return promises;
  }, []);
  return Promise.all(articlesWithAll);
}

function _updateArticleInDatabase(report) {
  const { addedArticles } = report;
  return shareImages(addedArticles) // todo prepare une validation d'image si manquante
    .then(articles => articleRepository.create(articles))
    .then(() => Promise.resolve(report)); // todo use a do()
}

function _ifArticlesChangesThenUpdateArticlesInDatabase(report) {
  const result = report; // todo : remove one var here
  if (result.hasChanges) {
    return _updateArticleInDatabase(report);
  }
  return Promise.resolve(result);
}

function _generateChapters(cuttedArticle) {
  const chapters = [];
  for (let i = 1; i < cuttedArticle.length / 3; i += 1) {
    const imgLink = `img${i}.jpg`;
    const title = cuttedArticle[(3 * i) - 2];
    const subtitle = cuttedArticle[(3 * i) - 1];
    chapters[i - 1] = {
      title: [title, subtitle].join(' ').trim(),
      imgLink,
      // TODO : add Paragraph from data in db -  text: this._addParagraphs(cuttedArticle[3 * i]),
      text: cuttedArticle[3 * i],
    };
  }
  return chapters;
}

function serializeChapters(rawArticle) {
  const cuttedArticle = rawArticle
    .split('*')
    .map(row => row.trim());

  const chapters = _generateChapters(cuttedArticle);

  return {
    title: cuttedArticle[0],
    chapters,
  };
}

function _shareOneImg(imgLink, articleId) {
  return DropboxClient.createSharedLink(`/${articleId}/${imgLink}`)
    .then(_transformToDownloadableLink);
}

function shareChapterImages(chapters, idArticle) {
  const chaptersWithSharableLink = chapters.chapters.reduce((promises, chapter) => {
    const promise = _shareOneImg(chapter.imgLink, idArticle);
    promises.push(promise);
    return promises;
  }, []);
  return Promise.all(chaptersWithSharableLink)
    .then((imgLinks) => {
      const newChapters = chapters;
      for (let i = 0; i < imgLinks.length; i += 1) {
        newChapters.chapters[i].imgLink = imgLinks[i];
      }
      return newChapters;
    });
}

function _getCompleteChaptersToShare(article) {
  const articleId = article.dropboxId;
  return DropboxClient.getTextFileStream(articleId) // todo understand why some articles (like 57) cannot be found by Dropbox
    .then(FileReader.read)
    .then(chaptersContent => serializeChapters(chaptersContent))
    .then(chapters => shareChapterImages(chapters, articleId))
    .then(chapters => chapters.chapters.map(chapter => Object.assign(chapter, { dropboxId: article.dropboxId }))); // todo : add to former method
}

// concat :: ([a],[a]) -> [a]
const concat = (xs, ys) =>
  xs.concat(ys);

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.map(f).reduce(concat, []);

// id :: a -> a
const id = x =>
  x;

// flatten :: [[a]] -> [a]
const flatten =
  concatMap(id);

function _updateChapterInDatabase({ addedArticles }) {
  const allChaptersToSave = addedArticles.reduce((promises, article) => {
    const chaptersOfThisArticleToSave = _getCompleteChaptersToShare(article);
    promises.push(chaptersOfThisArticleToSave);
    return promises;
  }, []);
  return Promise.all(allChaptersToSave)
    .then(allChapters => flatten(allChapters))
    .then(chapters => chapterRepository.createArticleChapters(chapters)); // todo : delete former rows of this article
}

function _ifArticlesChangesThenUpdateChaptersInDatabase(report) {
  const result = report;
  if (result.hasChanges) {
    return _updateChapterInDatabase(result)
      .then(() => Promise.resolve(result));
  }
  return Promise.resolve(result);
}

function sendArticlesChangedEmail(form) {
  const { receivers } = form;
  const template = articlesChangedEmailTemplate.compile(form);

  const options = {
    from: config.MAIL_FROM,
    fromName: 'RecontactMe',
    to: receivers,
    subject: '[RecontactMe] Il y a du nouveau sur le site !',
    template,
  };
  return mailJet.sendEmail(options);
}

function _ifArticlesChangedThenSendEmailToRecipients(report) {
  const result = report;
  if (result.hasChanges) {
    return subscriptionRepository.getAll()
      .then((subscriptions) => {
        result.receivers = subscriptions.map(({ email }) => email);
        return result;
      })
      .then(form => sendArticlesChangedEmail(form));
  }
  return Promise.resolve(result);
}

function synchronizeArticles() {
  return DropboxClient.getAllDropboxFoldersMetadatas()
    .then(serializeArticles)
    .then(fetchedArticles => _compareDropboxAndDatabaseArticles(fetchedArticles))
    .then(_ifArticlesChangesThenUpdateArticlesInDatabase)
    .then(_ifArticlesChangesThenUpdateChaptersInDatabase)
    .then(_ifArticlesChangedThenSendEmailToRecipients);
}

// todo extract methods

module.exports = {
  synchronizeArticles,
};
<commit_msg>feature(REC-25) : import flatten from lodash<commit_after>const FileReader = require('../infrastructure/external_services/file-reader');
const DropboxClient = require('../infrastructure/external_services/dropbox-client');
const mailJet = require('../infrastructure/mailing/mailjet');
const config = require('../infrastructure/config');
const { isEmpty, flatten } = require('lodash');
const articleRepository = require('../domain/repositories/article-repository');
const chapterRepository = require('../domain/repositories/chapter-repository');
const subscriptionRepository = require('../domain/repositories/subscription-repository');
const articlesChangedEmailTemplate = require('../infrastructure/mailing/articles-changed-email-template');

function serializeArticles(metadatas) {
  return metadatas
    .map(metadata => ({
      dropboxId: metadata.name,
      imgPath: `/${metadata.name}/img0.jpg`,
    }));
}

function _compareDropboxAndDatabaseArticles(freshArticles) {
  return articleRepository.getAll()
    .then((oldArticles) => {
      const addedArticles = freshArticles.reduce((accumulatedArticles, freshArticle) => {
        const matchedArticles = oldArticles.filter(({ dropboxId }) => dropboxId === freshArticle.dropboxId);
        if (matchedArticles.length === 0) {
          accumulatedArticles.push(freshArticle);
        }
        return accumulatedArticles;
      }, []);
      const hasChanges = !isEmpty(addedArticles);
      return Promise.resolve({ addedArticles, hasChanges });
    });
}

function _transformToDownloadableLink(response) {
  if (isEmpty(response)) {
    return '';
  }
  return response.url.replace(/.$/, '1');
}

function _shareImg(article) {
  return DropboxClient.createSharedLink(article.imgPath)
    .then(imgLink => ({
      dropboxId: article.dropboxId,
      imgLink: _transformToDownloadableLink(imgLink),
    }));
}

function shareImages(articles) {
  const articlesWithAll = articles.reduce((promises, article) => {
    const articleWithAll = _shareImg(article);
    promises.push(articleWithAll);
    return promises;
  }, []);
  return Promise.all(articlesWithAll);
}

function _updateArticleInDatabase(report) {
  const { addedArticles } = report;
  return shareImages(addedArticles) // todo prepare une validation d'image si manquante
    .then(articles => articleRepository.create(articles))
    .then(() => Promise.resolve(report)); // todo use a do()
}

function _ifArticlesChangesThenUpdateArticlesInDatabase(report) {
  const result = report; // todo : remove one var here
  if (result.hasChanges) {
    return _updateArticleInDatabase(report);
  }
  return Promise.resolve(result);
}

function _generateChapters(cuttedArticle) {
  const chapters = [];
  for (let i = 1; i < cuttedArticle.length / 3; i += 1) {
    const imgLink = `img${i}.jpg`;
    const title = cuttedArticle[(3 * i) - 2];
    const subtitle = cuttedArticle[(3 * i) - 1];
    chapters[i - 1] = {
      title: [title, subtitle].join(' ').trim(),
      imgLink,
      // TODO : add Paragraph from data in db -  text: this._addParagraphs(cuttedArticle[3 * i]),
      text: cuttedArticle[3 * i],
    };
  }
  return chapters;
}

function serializeChapters(rawArticle) {
  const cuttedArticle = rawArticle
    .split('*')
    .map(row => row.trim());

  const chapters = _generateChapters(cuttedArticle);

  return {
    title: cuttedArticle[0],
    chapters,
  };
}

function _shareOneImg(imgLink, articleId) {
  return DropboxClient.createSharedLink(`/${articleId}/${imgLink}`)
    .then(_transformToDownloadableLink);
}

function shareChapterImages(chapters, idArticle) {
  const chaptersWithSharableLink = chapters.chapters.reduce((promises, chapter) => {
    const promise = _shareOneImg(chapter.imgLink, idArticle);
    promises.push(promise);
    return promises;
  }, []);
  return Promise.all(chaptersWithSharableLink)
    .then((imgLinks) => {
      const newChapters = chapters;
      for (let i = 0; i < imgLinks.length; i += 1) {
        newChapters.chapters[i].imgLink = imgLinks[i];
      }
      return newChapters;
    });
}

function _getCompleteChaptersToShare(article) {
  const articleId = article.dropboxId;
  return DropboxClient.getTextFileStream(articleId) // todo understand why some articles (like 57) cannot be found by Dropbox
    .then(FileReader.read)
    .then(chaptersContent => serializeChapters(chaptersContent))
    .then(chapters => shareChapterImages(chapters, articleId))
    .then(chapters => chapters.chapters.map(chapter => Object.assign(chapter, { dropboxId: article.dropboxId }))); // todo : add to former method
}

function _updateChapterInDatabase({ addedArticles }) {
  const allChaptersToSave = addedArticles.reduce((promises, article) => {
    const chaptersOfThisArticleToSave = _getCompleteChaptersToShare(article);
    promises.push(chaptersOfThisArticleToSave);
    return promises;
  }, []);
  return Promise.all(allChaptersToSave)
    .then(allChapters => flatten(allChapters))
    .then(chapters => chapterRepository.createArticleChapters(chapters)); // todo : delete former rows of this article
}

function _ifArticlesChangesThenUpdateChaptersInDatabase(report) {
  const result = report;
  if (result.hasChanges) {
    return _updateChapterInDatabase(result)
      .then(() => Promise.resolve(result));
  }
  return Promise.resolve(result);
}

function sendArticlesChangedEmail(form) {
  const { receivers } = form;
  const template = articlesChangedEmailTemplate.compile(form);

  const options = {
    from: config.MAIL_FROM,
    fromName: 'RecontactMe',
    to: receivers,
    subject: '[RecontactMe] Il y a du nouveau sur le site !',
    template,
  };
  return mailJet.sendEmail(options);
}

function _ifArticlesChangedThenSendEmailToRecipients(report) {
  const result = report;
  if (result.hasChanges) {
    return subscriptionRepository.getAll()
      .then((subscriptions) => {
        result.receivers = subscriptions.map(({ email }) => email);
        return result;
      })
      .then(form => sendArticlesChangedEmail(form));
  }
  return Promise.resolve(result);
}

function synchronizeArticles() {
  return DropboxClient.getAllDropboxFoldersMetadatas()
    .then(serializeArticles)
    .then(fetchedArticles => _compareDropboxAndDatabaseArticles(fetchedArticles))
    .then(_ifArticlesChangesThenUpdateArticlesInDatabase)
    .then(_ifArticlesChangesThenUpdateChaptersInDatabase)
    .then(_ifArticlesChangedThenSendEmailToRecipients);
}

// todo extract methods

module.exports = {
  synchronizeArticles,
};
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

importClass(Packages.org.apache.lenya.cms.cocoon.flow.FlowHelper);
importClass(Packages.org.apache.excalibur.source.SourceResolver);
importClass(Packages.org.apache.lenya.cms.cocoon.source.SourceUtil);

/**
 * Provides general flow functions for document editing.
 * @version $Id$
 */

function checkoutDocument() {
}

function checkinDocument() {
}

/**
 * Saves a Source to the document in the current PageEnvelope.
 * TODO: Use nobby's new usecase fw in 2.0 branch. Exception handling e.g. display appropriate error pages.
 * @param sourceUri An URI of a Source providing the edited document data
 * @param useBuffer If "true", the source Source is read into a buffer before it is written to its final destination.
 * @param workflowEvent Name of the workflow event to trigger. Default is "edit".
 * @param noWorkflow If true, no workflow event will be triggered. Default is false.
 * @param noCheckin If true, the current document is not checked in. Default is false.
 * @param backup If true, a new revision is created on checkin. Default is true.
 * @param status Default int value is 204. Used to set the response status.
 * @param noStatus If true, then no response status will be set.
 */
function editDocument() {
    var usecaseName = cocoon.parameters["lenya.usecase"];

    var usecaseResolver;
    var usecase;

    try {
        var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
        var request = flowHelper.getRequest(cocoon);
        var sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request);

        usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
        usecase = usecaseResolver.resolve(sourceUrl, usecaseName);

        if (cocoon.log.isDebugEnabled()) {
            cocoon.log.debug("edit-document.js::editDocument() calling usecase methods, usecaseName [" + usecaseName + "], sourceUrl [" + sourceUrl + "]");
        }

        usecase.setSourceURL(sourceUrl);
        usecase.setName(usecaseName);

        usecase.checkPreconditions();
        usecase.lockInvolvedObjects();
        if (!usecase.getErrorMessages().isEmpty()) {
            throw new Exception("Checking pre-conditions or locking failed!");
        }
        else {
            usecase.execute();
            if (!usecase.getErrorMessages().isEmpty()) {
                throw new Exception("Saving failed!");
            }
        }

        if (cocoon.log.isDebugEnabled()) {
            cocoon.log.debug("edit-document.js::editDocument() after usecase.execute(), hasErrors ? " + usecase.hasErrors());
        }

        // FIXME: handle error messages
        //if (usecase.hasErrors()) {
        //}

        if(_getParameter("noStatus", "false") == "false")
            cocoon.sendStatus(_getParameter("status", 204));
        else
            cocoon.redirectTo(_getParameter("redirectUrl", "FIXME"));

    } catch (exception) {

	/* FIXME: This is unclean because the flow will not return a value
	   if there is an exception */
        cocoon.log.error("Can not edit document.", exception);

    }
    finally {
        /* done with usecase component, tell usecaseResolver to release it */
        if (usecaseResolver != null) {
            if (usecase != null) {
                usecaseResolver.release(usecase);
                usecase = undefined;
            }
            cocoon.releaseComponent(usecaseResolver);
        }
    }

}

function _getParameter(name, defaultValue) {
    if(cocoon.parameters[name])
        return cocoon.parameters[name];
    else
        return defaultValue;
}

<commit_msg>added deprecation note (file remains for now as it's being used by kupu and bxe)<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
  This file is deprecated. You should not base new code on it.
  It's still used by Kupu and BXE,  that's why it remains for now.
  In the future, generic editor functionality and document handling
  helpers should be provided by the editors module only.
*/

importClass(Packages.org.apache.lenya.cms.cocoon.flow.FlowHelper);
importClass(Packages.org.apache.excalibur.source.SourceResolver);
importClass(Packages.org.apache.lenya.cms.cocoon.source.SourceUtil);

/**
 * Provides general flow functions for document editing.
 * @version $Id$
 */

function checkoutDocument() {
}

function checkinDocument() {
}

/**
 * Saves a Source to the document in the current PageEnvelope.
 * TODO: Use nobby's new usecase fw in 2.0 branch. Exception handling e.g. display appropriate error pages.
 * @param sourceUri An URI of a Source providing the edited document data
 * @param useBuffer If "true", the source Source is read into a buffer before it is written to its final destination.
 * @param workflowEvent Name of the workflow event to trigger. Default is "edit".
 * @param noWorkflow If true, no workflow event will be triggered. Default is false.
 * @param noCheckin If true, the current document is not checked in. Default is false.
 * @param backup If true, a new revision is created on checkin. Default is true.
 * @param status Default int value is 204. Used to set the response status.
 * @param noStatus If true, then no response status will be set.
 */
function editDocument() {
    var usecaseName = cocoon.parameters["lenya.usecase"];

    var usecaseResolver;
    var usecase;

    try {
        var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
        var request = flowHelper.getRequest(cocoon);
        var sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request);

        usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
        usecase = usecaseResolver.resolve(sourceUrl, usecaseName);

        if (cocoon.log.isDebugEnabled()) {
            cocoon.log.debug("edit-document.js::editDocument() calling usecase methods, usecaseName [" + usecaseName + "], sourceUrl [" + sourceUrl + "]");
        }

        usecase.setSourceURL(sourceUrl);
        usecase.setName(usecaseName);

        usecase.checkPreconditions();
        usecase.lockInvolvedObjects();
        if (!usecase.getErrorMessages().isEmpty()) {
            throw new Exception("Checking pre-conditions or locking failed!");
        }
        else {
            usecase.execute();
            if (!usecase.getErrorMessages().isEmpty()) {
                throw new Exception("Saving failed!");
            }
        }

        if (cocoon.log.isDebugEnabled()) {
            cocoon.log.debug("edit-document.js::editDocument() after usecase.execute(), hasErrors ? " + usecase.hasErrors());
        }

        // FIXME: handle error messages
        //if (usecase.hasErrors()) {
        //}

        if(_getParameter("noStatus", "false") == "false")
            cocoon.sendStatus(_getParameter("status", 204));
        else
            cocoon.redirectTo(_getParameter("redirectUrl", "FIXME"));

    } catch (exception) {

	/* FIXME: This is unclean because the flow will not return a value
	   if there is an exception */
        cocoon.log.error("Can not edit document.", exception);

    }
    finally {
        /* done with usecase component, tell usecaseResolver to release it */
        if (usecaseResolver != null) {
            if (usecase != null) {
                usecaseResolver.release(usecase);
                usecase = undefined;
            }
            cocoon.releaseComponent(usecaseResolver);
        }
    }

}

function _getParameter(name, defaultValue) {
    if(cocoon.parameters[name])
        return cocoon.parameters[name];
    else
        return defaultValue;
}

<|endoftext|>


<commit_before>var _ = require('_'),

    _array = require('../../array'),
    _nodeType = require('../../../nodeType'),
    _cache = require('../../../cache'),
    _regex = require('../../../regex'),
    _supports = require('../../../supports'),

    _ID_PREFIX = 'D-uniqueId-',
    _querySelectorCache = _cache(),

    _isMatch = require('../selector/selector-match'),

    _GET_ELEMENT_BY_ID          = 'getElementById',
    _GET_ELEMENTS_BY_TAG_NAME   = 'getElementsByTagName',
    _GET_ELEMENTS_BY_CLASS_NAME = 'getElementsByClassName',
    _QUERY_SELECTOR_ALL         = 'querySelectorAll';

var _determineMethod = function(selector) {
        var method = _querySelectorCache.get(selector);
        if (method) { return method; }

        if (_regex.selector.isStrictId(selector)) {
            method = _GET_ELEMENT_BY_ID;
        } else if (_regex.selector.isClass(selector)) {
            method = _GET_ELEMENTS_BY_CLASS_NAME;
        } else if (_regex.selector.isTag(selector)) {
            method = _GET_ELEMENTS_BY_TAG_NAME;
        } else {
            method = _QUERY_SELECTOR_ALL;
        }

        _querySelectorCache.set(selector, method);
        return method;
    },
    _uniqueId = function() {
        return _ID_PREFIX + _.uniqueId();
    };

var Selector = function(str) {
    var selector = _.string.trim(str),
        isChildOrSiblingSelect = (selector[0] === '>' || selector[0] === '+'),
        method = isChildOrSiblingSelect ? _QUERY_SELECTOR_ALL : _determineMethod(selector);

    this.str = str;
    this.selector = selector;
    this.isChildOrSiblingSelect = isChildOrSiblingSelect;
    this.isIdSearch = method === _GET_ELEMENT_BY_ID;
    this.isClassSearch = !this.isIdSearch && method === _GET_ELEMENTS_BY_CLASS_NAME;
    this.method = method;
};

Selector.prototype = {

    _tailorChildSelector: function(id, selector) {
        return '#' + id + ' ' + selector;
    },

    match: function(context) {
        // No neeed to check, a match will fail if it's
        // child or sibling
        if (this.isChildOrSiblingSelect) { return false; }

        return _isMatch(context, this.selector);
    },

    exec: function(context) {
        context = context || document;

        var selection,
            method = this.method,
            nodeType;
        // Early return if context is not an element or document
        if ((nodeType = context.nodeType) !== _nodeType.ELEMENT && nodeType !== _nodeType.DOCUMENT) { return; }

        // Child select - needs special help so that "> div" doesn't break
        var id,
            newId,
            idApplied = false,
            selector = this.selector;
        if (this.isChildOrSiblingSelect) {
            id = context.id;
            if (!_.exists(id)) {
                newId = _uniqueId();
                context.id = newId;
                idApplied = true;
            }

            selector = this._tailorChildSelector(idApplied ? newId : id, selector);
            context = document;
        } else if (this.isClassSearch) {
            if (_supports.getElementsByClassName) {
                // Class search, don't start with '.'
                selector = selector.substr(1);
            } else {
                method = _QUERY_SELECTOR_ALL;
            }
        } else if (this.isIdSearch) {
            if (context === document) {
                // Id search, don't start with '#'
                selector = selector.substr(1);
            } else {
                // context is not the document,
                // change to querySelectorAll and
                // leave the '#'
                method = _QUERY_SELECTOR_ALL;
            }
        }

        selection = context[method](selector);

        if (idApplied) { context.id = id; }

        // No selection
        if (!selection) {
            return [];
        }
        // Nodelist without a length
        if (_.isNodeList(selection) && !selection.length) {
            return [];
        }
        // IE8 DispStaticNodeList
        if (selection.item && selection.length === 0) {
            return [];
        }

        // If it's an id, return it as an array, otherwise, toArray
        // to keep from returning out NodeLists and HTMLCollections
        return _.isElement(selection) || !selection.length ? [selection] : _.toArray(selection);
    }
};

module.exports = Selector;
<commit_msg>Minor/ignore - Removed unused import<commit_after>var _ = require('_'),

    _nodeType = require('../../../nodeType'),
    _cache = require('../../../cache'),
    _regex = require('../../../regex'),
    _supports = require('../../../supports'),

    _ID_PREFIX = 'D-uniqueId-',
    _querySelectorCache = _cache(),

    _isMatch = require('../selector/selector-match'),

    _GET_ELEMENT_BY_ID          = 'getElementById',
    _GET_ELEMENTS_BY_TAG_NAME   = 'getElementsByTagName',
    _GET_ELEMENTS_BY_CLASS_NAME = 'getElementsByClassName',
    _QUERY_SELECTOR_ALL         = 'querySelectorAll';

var _determineMethod = function(selector) {
        var method = _querySelectorCache.get(selector);
        if (method) { return method; }

        if (_regex.selector.isStrictId(selector)) {
            method = _GET_ELEMENT_BY_ID;
        } else if (_regex.selector.isClass(selector)) {
            method = _GET_ELEMENTS_BY_CLASS_NAME;
        } else if (_regex.selector.isTag(selector)) {
            method = _GET_ELEMENTS_BY_TAG_NAME;
        } else {
            method = _QUERY_SELECTOR_ALL;
        }

        _querySelectorCache.set(selector, method);
        return method;
    },
    _uniqueId = function() {
        return _ID_PREFIX + _.uniqueId();
    };

var Selector = function(str) {
    var selector = _.string.trim(str),
        isChildOrSiblingSelect = (selector[0] === '>' || selector[0] === '+'),
        method = isChildOrSiblingSelect ? _QUERY_SELECTOR_ALL : _determineMethod(selector);

    this.str = str;
    this.selector = selector;
    this.isChildOrSiblingSelect = isChildOrSiblingSelect;
    this.isIdSearch = method === _GET_ELEMENT_BY_ID;
    this.isClassSearch = !this.isIdSearch && method === _GET_ELEMENTS_BY_CLASS_NAME;
    this.method = method;
};

Selector.prototype = {

    _tailorChildSelector: function(id, selector) {
        return '#' + id + ' ' + selector;
    },

    match: function(context) {
        // No neeed to check, a match will fail if it's
        // child or sibling
        if (this.isChildOrSiblingSelect) { return false; }

        return _isMatch(context, this.selector);
    },

    exec: function(context) {
        context = context || document;

        var selection,
            method = this.method,
            nodeType;
        // Early return if context is not an element or document
        if ((nodeType = context.nodeType) !== _nodeType.ELEMENT && nodeType !== _nodeType.DOCUMENT) { return; }

        // Child select - needs special help so that "> div" doesn't break
        var id,
            newId,
            idApplied = false,
            selector = this.selector;
        if (this.isChildOrSiblingSelect) {
            id = context.id;
            if (!_.exists(id)) {
                newId = _uniqueId();
                context.id = newId;
                idApplied = true;
            }

            selector = this._tailorChildSelector(idApplied ? newId : id, selector);
            context = document;
        } else if (this.isClassSearch) {
            if (_supports.getElementsByClassName) {
                // Class search, don't start with '.'
                selector = selector.substr(1);
            } else {
                method = _QUERY_SELECTOR_ALL;
            }
        } else if (this.isIdSearch) {
            if (context === document) {
                // Id search, don't start with '#'
                selector = selector.substr(1);
            } else {
                // context is not the document,
                // change to querySelectorAll and
                // leave the '#'
                method = _QUERY_SELECTOR_ALL;
            }
        }

        selection = context[method](selector);

        if (idApplied) { context.id = id; }

        // No selection
        if (!selection) {
            return [];
        }
        // Nodelist without a length
        if (_.isNodeList(selection) && !selection.length) {
            return [];
        }
        // IE8 DispStaticNodeList
        if (selection.item && selection.length === 0) {
            return [];
        }

        // If it's an id, return it as an array, otherwise, toArray
        // to keep from returning out NodeLists and HTMLCollections
        return _.isElement(selection) || !selection.length ? [selection] : _.toArray(selection);
    }
};

module.exports = Selector;
<|endoftext|>