<commit_before>import { Factory, faker } from 'ember-cli-mirage';

export default Factory.extend({
  fund: () => faker.random.arrayElement(["WNYC", "WQXR", "Radiolab"]),
  orderPrice: () => faker.random.arrayElement([60, 72, 120, 12, 90, 100]),
  orderDate: faker.date.past,
  orderType: () => faker.random.arrayElement(["onetime", "sustainer"]),
  orderKey: () => faker.random.uuid(),
  premium: 'Brian Lehrer Animated Series',
  creditCardType: 'VISA',
  creditCardLast4Digits: '0302',
  isActive: faker.random.boolean
});
<commit_msg>Add isSustainer key to mirage pledge factory<commit_after>import { Factory, faker } from 'ember-cli-mirage';
import computed from 'ember-computed';

export default Factory.extend({
  fund: () => faker.random.arrayElement(["WNYC", "WQXR", "Radiolab"]),
  orderPrice: () => faker.random.arrayElement([60, 72, 120, 12, 90, 100]),
  orderDate: faker.date.past,
  orderType: () => faker.random.arrayElement(["onetime", "sustainer"]),
  orderKey: () => faker.random.uuid(),
  premium: 'Brian Lehrer Animated Series',
  creditCardType: 'VISA',
  creditCardLast4Digits: '0302',
  isActive: faker.random.boolean,
  isSustainer() {
    return this.orderType == 'sustainer' ? true : false;
  },
});
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import { DateTime } from 'luxon'

import { CalendarMap, CuratedEventListItem, StaticPage } from '../components'
import { asInfiniteListCalendar } from '../containers'
import { translate } from '../../../common/services/i18n'
import { withConfig } from '../containers'

const WrappedInfiniteList = asInfiniteListCalendar(CuratedEventListItem)

class Calendar extends Component {
  static propTypes = {
    config: PropTypes.object.isRequired,
    isActive: PropTypes.bool.isRequired,
    isAdmin: PropTypes.bool.isRequired,
    isAuthenticated: PropTypes.bool.isRequired,
    isParticipant: PropTypes.bool.isRequired,
    listItems: PropTypes.array.isRequired,
    push: PropTypes.func.isRequired,
  }

  static defaultProps = {
    listItems: [],
  }

  onClick(item) {
    this.props.push(`/events/${item.slug}`)
  }

  onEditClick(item) {
    this.props.push(`/events/${item.slug}/edit`)
  }

  onPreviewClick() {
    this.props.push('/tickets')
  }

  renderItemsList() {
    if ((!this.props.isAuthenticated || !this.props.isActive) && this.props.config.festivalTicketPrice !== 0) {
      return (
        <WrappedInfiniteList
          resourceName="preview"
          onClick={this.onPreviewClick}
        />
      )
    }

    return (
      <WrappedInfiniteList
        resourceName="events"
        onClick={this.onClick}
        onEditClick={this.onEditClick}
      />
    )
  }

  renderCreateButton() {
    if (
      !(this.props.isParticipant || this.props.isAdmin) ||
      !this.props.isAuthenticated
    ) {
      return null
    }

    return (
      <Link className="button button--green" to="/new/event">
        { translate('views.events.createNewButton') }
      </Link>
    )
  }

  renderText() {
    if ((!this.props.isAuthenticated || !this.props.isActive) && this.props.config.festivalTicketPrice !== 0) {
      return <StaticPage hideTitle={true} slug="calendar-public" />
    }

    return <StaticPage hideTitle={true} slug="calendar" />
  }

  renderMap() {
    if ((!this.props.isAuthenticated || !this.props.isActive) && this.props.config.festivalTicketPrice !== 0) {
      return null
    }

    const allEvents = this.props.listItems
    let uniqueVenues = []

    if (allEvents.length === 0) {
      return null
    }

    uniqueVenues = allEvents
      .map(event => event.placeId)
      .map((event, index, final) => final.indexOf(event) === index && index)
      .filter(event => allEvents[event]).map(event => allEvents[event].place)
      .filter(place => place.mode !== 'virtual')

    const mapVenuePlots = uniqueVenues.map(venue => {
      const venueEvents = allEvents.reduce((result, event) => {
        if (venue.id === event.placeId) {
          const time = DateTime.fromISO(event.slots[0].from).toFormat('T DDDD')
          result.push({
            title: event.title,
            time: time,
            imageUrl: event.images.length > 0 ? event.images[0].smallImageUrl : null,
            slug: event.slug,
          })
        }
        return result
      }, [])

      return {
        city: venue.city,
        cityCode: venue.cityCode,
        country: venue.country,
        key: venue.id,
        latitude: venue.latitude,
        longitude: venue.longitude,
        mode: venue.mode,
        street: venue.street,
        place: venue.title,
        events: venueEvents,
      }
    })
    return (

        <CalendarMap initialCenter= { { lat: this.props.config.defaultLatitude, lng: this.props.config.defaultLongitude } } plots={mapVenuePlots} />

    )
  }

  render() {
    return (

{ translate('views.events.calendarTitle') }

        { this.renderText() }
        { this.renderCreateButton() }

        { this.renderMap() }
        { this.renderItemsList() }

    )
  }

  constructor(props) {
    super(props)

    this.onClick = this.onClick.bind(this)
    this.onEditClick = this.onEditClick.bind(this)
    this.onPreviewClick = this.onPreviewClick.bind(this)
  }
}

function mapStateToProps(state) {
  return {
    ...state.auth,
    ...state.user,
    ...state.meta,
    ...state.infiniteList.events,
  }
}

export default connect(
  mapStateToProps, {
    push,
  }
)(withConfig(Calendar))
<commit_msg>Use unpaginated list of events for CalendarMap<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import { DateTime } from 'luxon'

import { CalendarMap, CuratedEventListItem, StaticPage } from '../components'
import { asInfiniteListCalendar } from '../containers'
import { translate } from '../../../common/services/i18n'
import { withConfig } from '../containers'

const WrappedInfiniteList = asInfiniteListCalendar(CuratedEventListItem)

class Calendar extends Component {
  static propTypes = {
    config: PropTypes.object.isRequired,
    isActive: PropTypes.bool.isRequired,
    isAdmin: PropTypes.bool.isRequired,
    isAuthenticated: PropTypes.bool.isRequired,
    isParticipant: PropTypes.bool.isRequired,
    resourceListItems: PropTypes.array.isRequired,
    push: PropTypes.func.isRequired,
  }

  static defaultProps = {
    resourceListItems: [],
  }

  onClick(item) {
    this.props.push(`/events/${item.slug}`)
  }

  onEditClick(item) {
    this.props.push(`/events/${item.slug}/edit`)
  }

  onPreviewClick() {
    this.props.push('/tickets')
  }

  renderItemsList() {
    if ((!this.props.isAuthenticated || !this.props.isActive) && this.props.config.festivalTicketPrice !== 0) {
      return (
        <WrappedInfiniteList
          resourceName="preview"
          onClick={this.onPreviewClick}
        />
      )
    }

    return (
      <WrappedInfiniteList
        resourceName="events"
        onClick={this.onClick}
        onEditClick={this.onEditClick}
      />
    )
  }

  renderCreateButton() {
    if (
      !(this.props.isParticipant || this.props.isAdmin) ||
      !this.props.isAuthenticated
    ) {
      return null
    }

    return (
      <Link className="button button--green" to="/new/event">
        { translate('views.events.createNewButton') }
      </Link>
    )
  }

  renderText() {
    if ((!this.props.isAuthenticated || !this.props.isActive) && this.props.config.festivalTicketPrice !== 0) {
      return <StaticPage hideTitle={true} slug="calendar-public" />
    }

    return <StaticPage hideTitle={true} slug="calendar" />
  }

  renderMap() {
    if ((!this.props.isAuthenticated || !this.props.isActive) && this.props.config.festivalTicketPrice !== 0) {
      return null
    }

    const allEvents = this.props.resourceListItems
    let uniqueVenues = []

    if (allEvents.length === 0) {
      return null
    }

    uniqueVenues = allEvents
      .map(event => event.placeId)
      .map((event, index, final) => final.indexOf(event) === index && index)
      .filter(event => allEvents[event]).map(event => allEvents[event].place)
      .filter(place => place.mode !== 'virtual')

    const mapVenuePlots = uniqueVenues.map(venue => {
      const venueEvents = allEvents.reduce((result, event) => {
        if (venue.id === event.placeId) {
          const time = DateTime.fromISO(event.slots[0].from).toFormat('T DDDD')
          result.push({
            title: event.title,
            time: time,
            imageUrl: event.images.length > 0 ? event.images[0].smallImageUrl : null,
            slug: event.slug,
          })
        }
        return result
      }, [])

      return {
        city: venue.city,
        cityCode: venue.cityCode,
        country: venue.country,
        key: venue.id,
        latitude: venue.latitude,
        longitude: venue.longitude,
        mode: venue.mode,
        street: venue.street,
        place: venue.title,
        events: venueEvents,
      }
    })
    return (

        <CalendarMap initialCenter= { { lat: this.props.config.defaultLatitude, lng: this.props.config.defaultLongitude } } plots={mapVenuePlots} />

    )
  }

  render() {
    return (

{ translate('views.events.calendarTitle') }

        { this.renderText() }
        { this.renderCreateButton() }

        { this.renderMap() }
        { this.renderItemsList() }

    )
  }

  constructor(props) {
    super(props)

    this.onClick = this.onClick.bind(this)
    this.onEditClick = this.onEditClick.bind(this)
    this.onPreviewClick = this.onPreviewClick.bind(this)
  }
}

function mapStateToProps(state) {
  return {
    ...state.auth,
    ...state.user,
    ...state.meta,
    ...state.resourceList,
  }
}

export default connect(
  mapStateToProps, {
    push,
  }
)(withConfig(Calendar))
<|endoftext|>


<commit_before>/*!
 * LetsBlog
 * Pre-execute functions of different page types
 * Released under MIT license
 */

'use strict';

var Promise = require('bluebird'),
	util = require('../lib/util'),
	optionsBLL = require('../bll/options'),
	categoryBLL = require('../bll/category'),
	linkBLL = require('../bll/link'),
	userModel = require('../entity/user'),
	userBLL = require('../bll/user'),
	userGroupBLL = require('../bll/usergroup');

// 在数组最前面插入元素
function prepend(elt, arr) {
	if ( Array.isArray(arr) ) {
		arr = arr.slice();
	} else if (arr) {
		arr = [arr];
	} else {
		arr = [ ];
	}

	if (elt) { arr.unshift(elt); }

	return arr;
}
exports.prepend = prepend;

// 基础页面
function basicPage(callbacks) {
	return prepend(function(req, res, next) {
		var user;

		return userBLL.readByUsernameAndPassword(
			req.cookies.username,
			req.cookies.password
		).then(function(result) {
			user = result;
		}).catch(function(e) {
			// error的情况下表示没有登录
			// 无需处理异常
		}).then(function() {
			// 如果用户未登录或不存在，则生成一个访客对象
			user = user || userModel.createEntity({
				userid: 0,
				username: '',
				groupid: 2
			});

			return userGroupBLL.read(user.groupid);
		}).then(function(result) {
			if (result) {
				user.group = result;
				req.currentUser = user;

				var userData = user.toPureData();
				// 删除较为敏感的密码信息，防止误输出
				delete userData.password;

				if (res.routeHelper.type() === 'html') {
					res.routeHelper.viewData('currentUser', userData);
				}

				if (user.userid) {
					// 更新用户最后在线状态
					return userBLL.updateActivity(req.ip, user.userid);
				}
			} else {
				throw util.createError('用户组不存在');
			}
		});
	}, callbacks);
}
exports.basic = basicPage;

// 标准页面
function normalPage(callbacks) {
	return basicPage(
		prepend(function(req, res, next) {
			var isHTMLPage = res.routeHelper.type() === 'html';

			if (isHTMLPage) { res.routeHelper.viewData('categoryid', -1); }

			var tasks = [
				// 加载网站设置
				optionsBLL.read().then(function(result) {
					if (!result) {
						err = util.createError('网站设置丢失', 500);
					} else if (!result.isopen) {
						err = util.createError(result.tipstext || '网站已关闭');
					} else {
						if (res.routeHelper.type() === 'html') {
							res.routeHelper.appendTitle(result.sitename);
							res.routeHelper.appendKeywords( result.keywords.split(/\s*,\s*/) );
							res.routeHelper.viewData({
								description: result.description,
								currentOptions: result,
								currentYear: ( new Date() ).getFullYear()
							});
						}
					}
				})
			];

			if (isHTMLPage) {
				tasks.push(
					// 加载可见分类
					categoryBLL.list(1).then(function(result) {
						res.routeHelper.viewData('categoryList', result);
					}),

					// 加载可见链接
					linkBLL.list(1).then(function(result) {
						res.routeHelper.viewData('linkList', result);
					})
				)
			}

			return Promise.all(tasks);
		}, callbacks)
	);
}
exports.normal = normalPage;

// 管理后台页面
function adminPage(callbacks) {
	return basicPage(
		prepend(function(req, res, next) {
			if (req.currentUser.userid) {
				next();
			} else {
				res.redirect(
					'/user/login' +
					'?referrer=' + encodeURIComponent(req.originalUrl) +
					'&msg=' + encodeURIComponent('请先登录')
				);
			}
		}, callbacks)
	);
}
exports.admin = adminPage;<commit_msg>首页分类id统一为0<commit_after>/*!
 * LetsBlog
 * Pre-execute functions of different page types
 * Released under MIT license
 */

'use strict';

var Promise = require('bluebird'),
	util = require('../lib/util'),
	optionsBLL = require('../bll/options'),
	categoryBLL = require('../bll/category'),
	linkBLL = require('../bll/link'),
	userModel = require('../entity/user'),
	userBLL = require('../bll/user'),
	userGroupBLL = require('../bll/usergroup');

// 在数组最前面插入元素
function prepend(elt, arr) {
	if ( Array.isArray(arr) ) {
		arr = arr.slice();
	} else if (arr) {
		arr = [arr];
	} else {
		arr = [ ];
	}

	if (elt) { arr.unshift(elt); }

	return arr;
}
exports.prepend = prepend;

// 基础页面
function basicPage(callbacks) {
	return prepend(function(req, res, next) {
		var user;

		return userBLL.readByUsernameAndPassword(
			req.cookies.username,
			req.cookies.password
		).then(function(result) {
			user = result;
		}).catch(function(e) {
			// error的情况下表示没有登录
			// 无需处理异常
		}).then(function() {
			// 如果用户未登录或不存在，则生成一个访客对象
			user = user || userModel.createEntity({
				userid: 0,
				username: '',
				groupid: 2
			});

			return userGroupBLL.read(user.groupid);
		}).then(function(result) {
			if (result) {
				user.group = result;
				req.currentUser = user;

				var userData = user.toPureData();
				// 删除较为敏感的密码信息，防止误输出
				delete userData.password;

				if (res.routeHelper.type() === 'html') {
					res.routeHelper.viewData('currentUser', userData);
				}

				if (user.userid) {
					// 更新用户最后在线状态
					return userBLL.updateActivity(req.ip, user.userid);
				}
			} else {
				throw util.createError('用户组不存在');
			}
		});
	}, callbacks);
}
exports.basic = basicPage;

// 标准页面
function normalPage(callbacks) {
	return basicPage(
		prepend(function(req, res, next) {
			var isHTMLPage = res.routeHelper.type() === 'html';

			if (isHTMLPage) { res.routeHelper.viewData('categoryid', 0); }

			var tasks = [
				// 加载网站设置
				optionsBLL.read().then(function(result) {
					if (!result) {
						err = util.createError('网站设置丢失', 500);
					} else if (!result.isopen) {
						err = util.createError(result.tipstext || '网站已关闭');
					} else {
						if (res.routeHelper.type() === 'html') {
							res.routeHelper.appendTitle(result.sitename);
							res.routeHelper.appendKeywords( result.keywords.split(/\s*,\s*/) );
							res.routeHelper.viewData({
								description: result.description,
								currentOptions: result,
								currentYear: ( new Date() ).getFullYear()
							});
						}
					}
				})
			];

			if (isHTMLPage) {
				tasks.push(
					// 加载可见分类
					categoryBLL.list(1).then(function(result) {
						res.routeHelper.viewData('categoryList', result);
					}),

					// 加载可见链接
					linkBLL.list(1).then(function(result) {
						res.routeHelper.viewData('linkList', result);
					})
				)
			}

			return Promise.all(tasks);
		}, callbacks)
	);
}
exports.normal = normalPage;

// 管理后台页面
function adminPage(callbacks) {
	return basicPage(
		prepend(function(req, res, next) {
			if (req.currentUser.userid) {
				next();
			} else {
				res.redirect(
					'/user/login' +
					'?referrer=' + encodeURIComponent(req.originalUrl) +
					'&msg=' + encodeURIComponent('请先登录')
				);
			}
		}, callbacks)
	);
}
exports.admin = adminPage;<|endoftext|>


<commit_before>'use strict';

/* Controllers */

var controllers = angular.module('myApp.controllers', []);

controllers.controller('NavCtrl', ['$scope', 'PageManager', 'Authentication', function ($scope, PageManager, Authentication) {
  $scope.currentUser = Authentication.getCurrentUser();

  $scope.userChangeObserverCallback = function (user) {
    $scope.currentUser = user;
  };
  Authentication.registerUserChangeCallback($scope.userChangeObserverCallback);

  $scope.getPages = function () {
    PageManager.getPages().then(function (response) {
      $scope.pages = response.data;
    });
  };

  $scope.getPages();

  $scope.pageObserverCallback = function (pages) {
    $scope.pages = pages;
  };
  PageManager.registerPageObserverCallback($scope.pageObserverCallback);

  $scope.setPageBeingViewed = function (page) {
    PageManager.setPageBeingViewed(page);
  }
}]);

controllers.controller('EditPageCtrl', ['$scope', 'PageManager', 'Authentication', '$location', function ($scope, PageManager, Authentication, $location) {
  $scope.page = $.extend({}, PageManager.getPageBeingEdited());

  $scope.saveEdit = function () {
    if (Authentication.getCurrentUser()) {
      PageManager.editPage($scope.page, Authentication.getCurrentUser().session).then(function (response) {
        if (response.status == 200) {
          PageManager.setPageBeingEdited(null);
          $location.path("/index");
        } else {
          var message = response.data.error || response.data.message;
          if (message == null || message.length < 1) {
            message = "Error code " + response.status;
          }
          $scope.editDeleteError = {Message: "Could not edit Page: " + message};
        }
      });
    } else {
      $scope.editDeleteError = {Message: "Must log in as an administrator to edit page."};
    }
  };

  $scope.deletePage = function () {
    if (Authentication.getCurrentUser()) {
      PageManager.deletePage($scope.page, Authentication.getCurrentUser().session).then(function (response) {
        if (response.status == 200) {
          PageManager.setPageBeingEdited(null);
          $location.path("/index");
        } else {
          var message = response.data.error || response.data.message;
          if (message == null || message.length < 1) {
            message = "Error code " + response.status;
          }
          $scope.editDeleteError = {Message: "Could not delete page: " + message};
        }
      });
    } else {
      $scope.editDeleteError = {Message: "Must log in as administrator to delete page"};
    }
  };

  $scope.cancelEdit = function () {
    PageManager.setPageBeingEdited(null);
    $location.path("/index");
  };
}]);

controllers.controller('AddPageCtrl', ['$scope', 'PageManager', 'Authentication', '$location', function ($scope, PageManager, Authentication, $location) {
  $scope.addPage = function () {
    PageManager.addPage($scope.page, Authentication.getCurrentUser().session).then(function (response) {
      if (response.status == 200) {
        $location.path('/index');
      } else {
        var message = response.data.error;
        if (message == null || message.length < 1) {
          message = "Error code " + response.status;
        }
        $scope.addError = {Message: "Could not add Page: " + message};
      }
    });
  };
}]);

controllers.controller('LoginCtrl', ['$scope', 'Authentication', function ($scope, Authentication) {
  $scope.loginInfo = null;
  $scope.currentUser = Authentication.getCurrentUser();

  $scope.userChangeCallback = function (user) {
    $scope.currentUser = user;
  };

  Authentication.registerUserChangeCallback($scope.userChangeCallback);

  $scope.login = function () {
    Authentication.login($scope.loginInfo.email, $scope.loginInfo.password).then(function (response) {
      if (response.status != 200) {
        var message = response.data.error;
        if (message == null || message.length < 1) {
          message = "Error code " + response.status;
        }
        $scope.loginError = {Message: "Could not log in: " + message};
      }
    });
  };

  $scope.logout = function () {
    Authentication.logout();
  }

}]);

controllers.controller('PageDetailsCtrl', ['$scope', 'PageManager', 'Authentication', '$location', '$sce', function ($scope, PageManager, Authentication, $location, $sce) {
  $scope.page = PageManager.getPageBeingViewed();
  $scope.currentUser = Authentication.getCurrentUser();

  $scope.userChangeObserverCallback = function (user) {
    $scope.currentUser = user;
    $scope.getCurrentUserRatingForPage();
  };
  $scope.pageBeingViewedObserverCallback = function (pageBeingViewed) {
    $scope.page = pageBeingViewed;
  };
  PageManager.registerPageBeingViewedObserverCallback($scope.pageBeingViewedObserverCallback);
  Authentication.registerUserChangeCallback($scope.userChangeObserverCallback);

  $scope.editPage = function () {
    PageManager.setPageBeingEdited($scope.page);
    $location.path('editPage');
  };

  $scope.getPageDescription = function () {
    return $sce.trustAsHtml($scope.page.description);
  };

  $scope.$watch('rating', function () {
    if ($scope.rating && $scope.rating != $scope.previousRating) {
      PageManager.ratePage($scope.page, $scope.rating, Authentication.getCurrentUser().session).then(function (response) {
        if (response.status != 200) {
          var message = response.data.error || response.data.message;
          if (message == null || message.length < 1) {
            message = "Error code " + response.status;
          }
          $scope.rateError = {Message: "Could not rate Page: " + message};
          $scope.rating = $scope.previousRating;
        } else {
          $scope.previousRating = $scope.rating;
          $scope.page.rating = response.data.rating;
        }
      });
    } else {
      $scope.previousRating = $scope.rating;
    }
  });

  $scope.$watch('page', function () {
    $scope.getCurrentUserRatingForPage();
  });

  $scope.getCurrentUserRatingForPage = function () {
    if ($scope.page && $scope.currentUser) {
      PageManager.getSessionRatingForPage($scope.page, Authentication.getCurrentUser().session).then(function (response) {
        if (response.status == 200) {
          $scope.previousRating = response.data.rating;
          if (response.data.rating > 0) {
            $scope.rating = response.data.rating;
          }
        } else {
          $scope.rating = null;
          $scope.previousRating = 0;
          var message = response.data.error || response.data.message;
          if (message == null || message.length < 1) {
            message = "Error code " + response.status;
          }
          $scope.rateError = {Message: "Could not retrieve rating for user: " + message};
        }
      });
    }
  }

}]);

controllers.controller('HeaderCtrl', ['$scope', '$location', function ($scope, $location) {
  $scope.goHome = function () {
    $location.path('/welcome');
  }
}]);

controllers.controller('CookieCtrl', ['Authentication', 'PageManager', function (Authentication, PageManager) {
  function pageBeingViewedObserverCallback(page) {
    $.cookie('pageBeingViewed', JSON.stringify(page));
  }

  function userChangeObserverCallback(user) {
    $.cookie('currentApplicationUser', JSON.stringify(user));
  }

  PageManager.registerPageBeingViewedObserverCallback(pageBeingViewedObserverCallback);
  Authentication.registerUserChangeCallback(userChangeObserverCallback);
}]);

controllers.controller('UserManagementCtrl', ['Authentication', 'UserManagement', '$modal', '$scope', '$location',
  function (Authentication, UserManagement, $modal, $scope, $location) {
    $scope.userRoles = ['ADMIN', 'VIEWER'];

    $scope.userChangeObserverCallback = function () {
      if ($location.path() == '/userManagement') {
        $scope.getUsers();
      }
    };
    Authentication.registerUserChangeCallback($scope.userChangeObserverCallback);

    $scope.getUsers = function () {
      if (Authentication.getCurrentUser()) {
        UserManagement.getUserList(Authentication.getCurrentUser().session).then(function (response) {
          if (response.status == 200) {
            $scope.users = response.data;
          } else {
            var message = response.data.error || response.data.message;
            if (message == null || message.length < 1) {
              message = "Error code " + response.status;
            }
            $scope.showModal({title: 'Error', message: "Could not retrieve users: " + message});
          }
        })
      } else {
        $scope.showModal({title: 'Error', message: "Must be logged in as administrator to edit users."});
        $scope.users = null;
      }

    };
    $scope.getUsers();

    $scope.deleteUser = function (user) {
      if (Authentication.getCurrentUser()) {
        if (user.email == Authentication.getCurrentUser().email) {
          $scope.deleteError = {Message: "Cannot delete yourself"};
        } else {
          UserManagement.deleteUser(user, Authentication.getCurrentUser().session).then(function (response) {
            if (response.status == 200) {
              $.each($scope.users, function (index) {
                if (this.email == response.data.email) {
                  $scope.users.splice(index, 1);
                  return false;
                }
              });
              $scope.deleteSuccess = true;
            } else {
              var message = response.data.error || response.data.message;
              if (message == null || message.length < 1) {
                message = "Error code " + response.status;
              }
              $scope.showModal({title: 'Error', message: "Could not delete user: " + message});
            }
          });
        }
      } else {
        $scope.showModal({title: 'Log in', message: "You need to log in as administrator to delete a user"});
      }
    };

    $scope.saveUser = function (user) {
      if (Authentication.getCurrentUser()) {
        UserManagement.editUser(user, Authentication.getCurrentUser().session).then(function (response) {
          if (response.status == 200) {
            $scope.showModal({title: 'Success', message: 'User saved successfully'})
          } else {
            var message = response.data.error || response.data.message;
            if (message == null || message.length < 1) {
              message = "Error code " + response.status;
            }
            $scope.showModal({title: 'Error', message: "Could not edit user: " + message});
          }
        });
      } else {
        $scope.showModal({title: 'Log in', message: "You need to log in as administrator to edit a user's role"});
      }
    };

    $scope.showModal = function (message) {
      $modal.open({
        templateUrl: 'partials/modal_message.html',
        controller: function ($scope, $modalInstance, message) {
          $scope.message = message;
          $scope.dismiss = function () {
            $modalInstance.dismiss();
          }
        },
        resolve: {
          message: function () {
            return message;
          }
        }
      });
    }
  }]);
<commit_msg>clear login error on successful login. also remove from users list on delete page based on user email passed in rather than response data<commit_after>'use strict';

/* Controllers */

var controllers = angular.module('myApp.controllers', []);

controllers.controller('NavCtrl', ['$scope', 'PageManager', 'Authentication', function ($scope, PageManager, Authentication) {
  $scope.currentUser = Authentication.getCurrentUser();

  $scope.userChangeObserverCallback = function (user) {
    $scope.currentUser = user;
  };
  Authentication.registerUserChangeCallback($scope.userChangeObserverCallback);

  $scope.getPages = function () {
    PageManager.getPages().then(function (response) {
      $scope.pages = response.data;
    });
  };

  $scope.getPages();

  $scope.pageObserverCallback = function (pages) {
    $scope.pages = pages;
  };
  PageManager.registerPageObserverCallback($scope.pageObserverCallback);

  $scope.setPageBeingViewed = function (page) {
    PageManager.setPageBeingViewed(page);
  }
}]);

controllers.controller('EditPageCtrl', ['$scope', 'PageManager', 'Authentication', '$location', function ($scope, PageManager, Authentication, $location) {
  $scope.page = $.extend({}, PageManager.getPageBeingEdited());

  $scope.saveEdit = function () {
    if (Authentication.getCurrentUser()) {
      PageManager.editPage($scope.page, Authentication.getCurrentUser().session).then(function (response) {
        if (response.status == 200) {
          PageManager.setPageBeingEdited(null);
          $location.path("/index");
        } else {
          var message = response.data.error || response.data.message;
          if (message == null || message.length < 1) {
            message = "Error code " + response.status;
          }
          $scope.editDeleteError = {Message: "Could not edit Page: " + message};
        }
      });
    } else {
      $scope.editDeleteError = {Message: "Must log in as an administrator to edit page."};
    }
  };

  $scope.deletePage = function () {
    if (Authentication.getCurrentUser()) {
      PageManager.deletePage($scope.page, Authentication.getCurrentUser().session).then(function (response) {
        if (response.status == 200) {
          PageManager.setPageBeingEdited(null);
          $location.path("/index");
        } else {
          var message = response.data.error || response.data.message;
          if (message == null || message.length < 1) {
            message = "Error code " + response.status;
          }
          $scope.editDeleteError = {Message: "Could not delete page: " + message};
        }
      });
    } else {
      $scope.editDeleteError = {Message: "Must log in as administrator to delete page"};
    }
  };

  $scope.cancelEdit = function () {
    PageManager.setPageBeingEdited(null);
    $location.path("/index");
  };
}]);

controllers.controller('AddPageCtrl', ['$scope', 'PageManager', 'Authentication', '$location', function ($scope, PageManager, Authentication, $location) {
  $scope.addPage = function () {
    PageManager.addPage($scope.page, Authentication.getCurrentUser().session).then(function (response) {
      if (response.status == 200) {
        $location.path('/index');
      } else {
        var message = response.data.error;
        if (message == null || message.length < 1) {
          message = "Error code " + response.status;
        }
        $scope.addError = {Message: "Could not add Page: " + message};
      }
    });
  };
}]);

controllers.controller('LoginCtrl', ['$scope', 'Authentication', function ($scope, Authentication) {
  $scope.loginInfo = null;
  $scope.currentUser = Authentication.getCurrentUser();

  $scope.userChangeCallback = function (user) {
    $scope.currentUser = user;
  };

  Authentication.registerUserChangeCallback($scope.userChangeCallback);

  $scope.login = function () {
    Authentication.login($scope.loginInfo.email, $scope.loginInfo.password).then(function (response) {
      if (response.status == 200) {
        $scope.loginError = null;
      } else {
        var message = response.data.error;
        if (message == null || message.length < 1) {
          message = "Error code " + response.status;
        }
        $scope.loginError = {Message: "Could not log in: " + message};
      }
    });
  };

  $scope.logout = function () {
    Authentication.logout();
  }

}]);

controllers.controller('PageDetailsCtrl', ['$scope', 'PageManager', 'Authentication', '$location', '$sce', function ($scope, PageManager, Authentication, $location, $sce) {
  $scope.page = PageManager.getPageBeingViewed();
  $scope.currentUser = Authentication.getCurrentUser();

  $scope.userChangeObserverCallback = function (user) {
    $scope.currentUser = user;
    $scope.getCurrentUserRatingForPage();
  };
  $scope.pageBeingViewedObserverCallback = function (pageBeingViewed) {
    $scope.page = pageBeingViewed;
  };
  PageManager.registerPageBeingViewedObserverCallback($scope.pageBeingViewedObserverCallback);
  Authentication.registerUserChangeCallback($scope.userChangeObserverCallback);

  $scope.editPage = function () {
    PageManager.setPageBeingEdited($scope.page);
    $location.path('editPage');
  };

  $scope.getPageDescription = function () {
    return $sce.trustAsHtml($scope.page.description);
  };

  $scope.$watch('rating', function () {
    if ($scope.rating && $scope.rating != $scope.previousRating) {
      PageManager.ratePage($scope.page, $scope.rating, Authentication.getCurrentUser().session).then(function (response) {
        if (response.status != 200) {
          var message = response.data.error || response.data.message;
          if (message == null || message.length < 1) {
            message = "Error code " + response.status;
          }
          $scope.rateError = {Message: "Could not rate Page: " + message};
          $scope.rating = $scope.previousRating;
        } else {
          $scope.previousRating = $scope.rating;
          $scope.page.rating = response.data.rating;
        }
      });
    } else {
      $scope.previousRating = $scope.rating;
    }
  });

  $scope.$watch('page', function () {
    $scope.getCurrentUserRatingForPage();
  });

  $scope.getCurrentUserRatingForPage = function () {
    if ($scope.page && $scope.currentUser) {
      PageManager.getSessionRatingForPage($scope.page, Authentication.getCurrentUser().session).then(function (response) {
        if (response.status == 200) {
          $scope.previousRating = response.data.rating;
          if (response.data.rating > 0) {
            $scope.rating = response.data.rating;
          }
        } else {
          $scope.rating = null;
          $scope.previousRating = 0;
          var message = response.data.error || response.data.message;
          if (message == null || message.length < 1) {
            message = "Error code " + response.status;
          }
          $scope.rateError = {Message: "Could not retrieve rating for user: " + message};
        }
      });
    }
  }

}]);

controllers.controller('HeaderCtrl', ['$scope', '$location', function ($scope, $location) {
  $scope.goHome = function () {
    $location.path('/welcome');
  }
}]);

controllers.controller('CookieCtrl', ['Authentication', 'PageManager', function (Authentication, PageManager) {
  function pageBeingViewedObserverCallback(page) {
    $.cookie('pageBeingViewed', JSON.stringify(page));
  }

  function userChangeObserverCallback(user) {
    $.cookie('currentApplicationUser', JSON.stringify(user));
  }

  PageManager.registerPageBeingViewedObserverCallback(pageBeingViewedObserverCallback);
  Authentication.registerUserChangeCallback(userChangeObserverCallback);
}]);

controllers.controller('UserManagementCtrl', ['Authentication', 'UserManagement', '$modal', '$scope', '$location',
  function (Authentication, UserManagement, $modal, $scope, $location) {
    $scope.userRoles = ['ADMIN', 'VIEWER'];

    $scope.userChangeObserverCallback = function () {
      if ($location.path() == '/userManagement') {
        $scope.getUsers();
      }
    };
    Authentication.registerUserChangeCallback($scope.userChangeObserverCallback);

    $scope.getUsers = function () {
      if (Authentication.getCurrentUser()) {
        UserManagement.getUserList(Authentication.getCurrentUser().session).then(function (response) {
          if (response.status == 200) {
            $scope.users = response.data;
          } else {
            var message = response.data.error || response.data.message;
            if (message == null || message.length < 1) {
              message = "Error code " + response.status;
            }
            $scope.showModal({title: 'Error', message: "Could not retrieve users: " + message});
          }
        })
      } else {
        $scope.showModal({title: 'Error', message: "Must be logged in as administrator to edit users."});
        $scope.users = null;
      }

    };
    $scope.getUsers();

    $scope.deleteUser = function (user) {
      if (Authentication.getCurrentUser()) {
        if (user.email == Authentication.getCurrentUser().email) {
          $scope.deleteError = {Message: "Cannot delete yourself"};
        } else {
          UserManagement.deleteUser(user, Authentication.getCurrentUser().session).then(function (response) {
            if (response.status == 200) {
              $.each($scope.users, function (index) {
                if (this.email == user.email) {
                  $scope.users.splice(index, 1);
                  return false;
                }
              });
              $scope.deleteSuccess = true;
            } else {
              var message = response.data.error || response.data.message;
              if (message == null || message.length < 1) {
                message = "Error code " + response.status;
              }
              $scope.showModal({title: 'Error', message: "Could not delete user: " + message});
            }
          });
        }
      } else {
        $scope.showModal({title: 'Log in', message: "You need to log in as administrator to delete a user"});
      }
    };

    $scope.saveUser = function (user) {
      if (Authentication.getCurrentUser()) {
        UserManagement.editUser(user, Authentication.getCurrentUser().session).then(function (response) {
          if (response.status == 200) {
            $scope.showModal({title: 'Success', message: 'User saved successfully'})
          } else {
            var message = response.data.error || response.data.message;
            if (message == null || message.length < 1) {
              message = "Error code " + response.status;
            }
            $scope.showModal({title: 'Error', message: "Could not edit user: " + message});
          }
        });
      } else {
        $scope.showModal({title: 'Log in', message: "You need to log in as administrator to edit a user's role"});
      }
    };

    $scope.showModal = function (message) {
      $modal.open({
        templateUrl: 'partials/modal_message.html',
        controller: function ($scope, $modalInstance, message) {
          $scope.message = message;
          $scope.dismiss = function () {
            $modalInstance.dismiss();
          }
        },
        resolve: {
          message: function () {
            return message;
          }
        }
      });
    }
  }]);
<|endoftext|>


<commit_before>var concat = require('../');
var should = require('should');
var os = require('os');
var path = require('path');
var File = require('gulp-util').File;
var Buffer = require('buffer').Buffer;
require('mocha');

describe('gulp-concat', function() {
  describe('concat()', function() {

    testFiles(concat('test.js'                   ), ['wadup'], 'wadup');
    testFiles(concat('test.js', {newLine: '\r\n'}), ['wadup'], 'wadup');
    testFiles(concat('test.js', {newLine: ''    }), ['wadup'], 'wadup');

    testFiles(concat('test.js'                   ), ['wadup', 'doe'], 'wadup\ndoe');
    testFiles(concat('test.js', {newLine: '\r\n'}), ['wadup', 'doe'], 'wadup\r\ndoe');
    testFiles(concat('test.js', {newLine: ''    }), ['wadup', 'doe'], 'wadupdoe');

    testFiles(concat('test.js'                   ), ['wadup', 'doe', 'hey'], 'wadup\ndoe\nhey');
    testFiles(concat('test.js', {newLine: '\r\n'}), ['wadup', 'doe', 'hey'], 'wadup\r\ndoe\r\nhey');
    testFiles(concat('test.js', {newLine: ''    }), ['wadup', 'doe', 'hey'], 'wadupdoehey');

    function testFiles(stream, contentses, result) {
      it('should concat one or several files', function(done) {

        stream.on('data', function(newFile){
          should.exist(newFile);
          should.exist(newFile.path);
          should.exist(newFile.relative);
          should.exist(newFile.contents);

          var newFilePath = path.resolve(newFile.path);
          var expectedFilePath = path.resolve('/home/contra/test/test.js');
          newFilePath.should.equal(expectedFilePath);

          newFile.relative.should.equal('test.js');
          String(newFile.contents).should.equal(result);
          Buffer.isBuffer(newFile.contents).should.equal(true);
          done();
        });

        contentses.forEach(function(contents, i) {
          stream.write(new File({
            cwd: '/home/contra/',
            base: '/home/contra/test',
            path: '/home/contra/test/file' + i.toString() + '.js',
            contents: new Buffer(contents)
          }));
        });

        stream.end();

      });
    };

  });
});
<commit_msg>Removed spacing<commit_after>var concat = require('../');
var should = require('should');
var os = require('os');
var path = require('path');
var File = require('gulp-util').File;
var Buffer = require('buffer').Buffer;
require('mocha');

describe('gulp-concat', function() {
  describe('concat()', function() {
    testFiles(concat('test.js'), ['wadup'], 'wadup');
    testFiles(concat('test.js', {newLine: '\r\n'}), ['wadup'], 'wadup');
    testFiles(concat('test.js', {newLine: ''}), ['wadup'], 'wadup');

    testFiles(concat('test.js'), ['wadup', 'doe'], 'wadup\ndoe');
    testFiles(concat('test.js', {newLine: '\r\n'}), ['wadup', 'doe'], 'wadup\r\ndoe');
    testFiles(concat('test.js', {newLine: ''}), ['wadup', 'doe'], 'wadupdoe');

    testFiles(concat('test.js'), ['wadup', 'doe', 'hey'], 'wadup\ndoe\nhey');
    testFiles(concat('test.js', {newLine: '\r\n'}), ['wadup', 'doe', 'hey'], 'wadup\r\ndoe\r\nhey');
    testFiles(concat('test.js', {newLine: ''}), ['wadup', 'doe', 'hey'], 'wadupdoehey');

    function testFiles(stream, contentses, result) {
      it('should concat one or several files', function(done) {
        stream.on('data', function(newFile){
          should.exist(newFile);
          should.exist(newFile.path);
          should.exist(newFile.relative);
          should.exist(newFile.contents);

          var newFilePath = path.resolve(newFile.path);
          var expectedFilePath = path.resolve('/home/contra/test/test.js');
          newFilePath.should.equal(expectedFilePath);

          newFile.relative.should.equal('test.js');
          String(newFile.contents).should.equal(result);
          Buffer.isBuffer(newFile.contents).should.equal(true);
          done();
        });

        contentses.forEach(function(contents, i) {
          stream.write(new File({
            cwd: '/home/contra/',
            base: '/home/contra/test',
            path: '/home/contra/test/file' + i.toString() + '.js',
            contents: new Buffer(contents)
          }));
        });

        stream.end();
      });
    };
  });
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Attack's service.
 * Define all Attack related business logic.
 *
 * @author jun.thong@me.com (Jun Thong)
 */

let logger              = require('winston'),
    validator           = require('validator'),
    config              = require('config'),
    seqErrors           = require('../../node_modules/sequelize/lib/errors'),
    models              = require('../models/index.js');

/**
 * Get the last 10 attacks.
 *
 * @returns {Promise.<*>}
 */
module.exports.getLastAttacks = async () => {
    logger.debug('attackService.getLastAttacks()');

    // here we'll use a manual sql query for an optimized usage of postgreSQL.
    // Attacks table stores the involded kaiju's ids in an array, this way we can make an up to 5 time faster query
    // comparing to use a more classic join table. The used ORM doesn't support this functionality, causing me to forge
    // this query manually.
    // Also, using json_agg() and json_build_object() is faster and cleaner than building the same json object at
    // the application level.
    // The query could be even more optimised, but i target a MVP for this showcase.
    let query = 'SELECT a.id, json_agg(json_build_object(\'id\', k.id, \'name\', k.name, \'nameRomaji\', k."nameRomanji")) AS kaijus, a.location, a."beginAt", a."finishAt", u.nickname ' +
    'FROM "Attacks" AS a ' +
    'LEFT JOIN "Kaijus" AS k ON k.id = ANY(a.kaijus) ' +
    'LEFT JOIN "Users" AS u ON u.id = a."createdBy" ' +
    'GROUP BY a.id, u.nickname ' +
    'ORDER BY a."beginAt" DESC ' +
    'LIMIT 10';

    return await models.sequelize.query(query, { type: models.sequelize.QueryTypes.SELECT });
};

/**
 * Get attack around user.
 * The radius used is set in config file using meters.
 *
 * @param {Number} lat
 * @param {Number} lon
 * @returns {Promise.<*>}
 */
module.exports.getAttackAroundUser = async (lat, lon) => {
    // Because I'll skip some sequelize sugar, i'll have to validate data manually.
    // Validator module only accept string, so i'll cast lat/lon to string just in case.
    // Also, i'll use min/max to check if it's a valid coordinate.
    if(validator.isFloat(lat + '', { min: -90, max: 90 }) || validator.isFloat(lon + '', { min: -180, max: 180 })){
        // Why create a validation error myself when sequelize come with one built in ?
        // Allow me to keep a more coherent error handling.
        // Usually i'll provide an error message from an i18n system, but i'll do without to keep this project simple.
        throw new seqErrors.ValidationError();
    }

    // @see attackService.getLastAttacks() for comment on optimization.
    // Also ORMs are not really good to query geoData...
    let query = 'SELECT a.id, json_agg(json_build_object(\'id\', k.id, \'name\', k.name, \'nameRomaji\', k."nameRomanji")) AS kaijus, a.location, a."beginAt", a."finishAt", u.nickname ' +
    'FROM "Attacks" AS a ' +
    'LEFT JOIN "Kaijus" AS k ON k.id = ANY(a.kaijus) ' +
    'LEFT JOIN "Users" AS u ON u.id = a."createdBy" ' +
    'WHERE ST_Distance_Sphere(ST_MakePoint(' + lat + ',' + lon + '), a.location) <= ' + config.get('APP.SEARCH_RADIUS') + '' +
    'GROUP BY a.id, u.nickname ' +
    'ORDER BY a."beginAt" DESC ' +
    'LIMIT 10';

    return await models.sequelize.query(query, { type: models.sequelize.QueryTypes.SELECT });
};

/**
 * Create an attack.
 *
 * @param {Object} data
 * @returns {Promise.<void>}
 */
module.exports.create = async (data) => {
    await models.Attack.create({
        kaijus: data.kaijus,
        beginAt: data.beginAt,
        finishAt: data.finishAt,
        location: data.location,
        createdBy: data.createdBy
    });
};

/**
 * Delete an attack.
 *
 * @param {String} id
 * @returns {Promise.<void>}
 */
module.exports.delete = async (id) => {
    await models.Attack.destroy({where: {
        id: id
    }});
};

/**
 * Update an attack.
 *
 * @param {Object} data
 * @returns {Promise.<void>}
 */
module.exports.update = async (data) => {
    await models.Attack.update({
        kaijus: data.kaijus,
        beginAt: data.beginAt,
        finishAt: data.finishAt,
        location: data.location,
        createdBy: data.createdBy
    }, {
        where: {
            id: data.id
        }
    });
};
<commit_msg>fix: small bug<commit_after>'use strict';

/**
 * Attack's service.
 * Define all Attack related business logic.
 *
 * @author jun.thong@me.com (Jun Thong)
 */

let logger              = require('winston'),
    validator           = require('validator'),
    config              = require('config'),
    seqErrors           = require('../../node_modules/sequelize/lib/errors'),
    models              = require('../models/index.js');

/**
 * Get the last 10 attacks.
 *
 * @returns {Promise.<*>}
 */
module.exports.getLastAttacks = async () => {
    logger.debug('attackService.getLastAttacks()');

    // here we'll use a manual sql query for an optimized usage of postgreSQL.
    // Attacks table stores the involded kaiju's ids in an array, this way we can make an up to 5 time faster query
    // comparing to use a more classic join table. The used ORM doesn't support this functionality, causing me to forge
    // this query manually.
    // Also, using json_agg() and json_build_object() is faster and cleaner than building the same json object at
    // the application level.
    // The query could be even more optimised, but i target a MVP for this showcase.
    let query = 'SELECT a.id, json_agg(json_build_object(\'id\', k.id, \'name\', k.name, \'nameRomaji\', k."nameRomanji")) AS kaijus, a.location, a."beginAt", a."finishAt", u.nickname ' +
    'FROM "Attacks" AS a ' +
    'LEFT JOIN "Kaijus" AS k ON k.id = ANY(a.kaijus) ' +
    'LEFT JOIN "Users" AS u ON u.id = a."createdBy" ' +
    'GROUP BY a.id, u.nickname ' +
    'ORDER BY a."beginAt" DESC ' +
    'LIMIT 10';

    return await models.sequelize.query(query, { type: models.sequelize.QueryTypes.SELECT });
};

/**
 * Get attack around user.
 * The radius used is set in config file using meters.
 *
 * @param {Number} lat
 * @param {Number} lon
 * @returns {Promise.<*>}
 */
module.exports.getAttackAroundUser = async (lat, lon) => {
    logger.debug('attackService.getLastAttacks(lat: ' + lat + ', lon : ' + lon + ')');
    // Because I'll skip some sequelize sugar, i'll have to validate data manually.
    // Validator module only accept string, so i'll cast lat/lon to string just in case.
    // Also, i'll use min/max to check if it's a valid coordinate.
    if(!validator.isFloat(lat + '', { min: -90, max: 90 }) || !validator.isFloat(lon + '', { min: -180, max: 180 })){
        // Why create a validation error myself when sequelize come with one built in ?
        // Allow me to keep a more coherent error handling.
        // Usually i'll provide an error message from an i18n system, but i'll do without to keep this project simple.
        throw new seqErrors.ValidationError();
    }

    // @see attackService.getLastAttacks() for comment on optimization.
    // Also ORMs are not really good to query geoData...
    let query = 'SELECT a.id, json_agg(json_build_object(\'id\', k.id, \'name\', k.name, \'nameRomaji\', k."nameRomanji")) AS kaijus, a.location, a."beginAt", a."finishAt", u.nickname ' +
    'FROM "Attacks" AS a ' +
    'LEFT JOIN "Kaijus" AS k ON k.id = ANY(a.kaijus) ' +
    'LEFT JOIN "Users" AS u ON u.id = a."createdBy" ' +
    'WHERE ST_Distance_Sphere(ST_MakePoint(' + lat + ',' + lon + '), a.location) <= ' + config.get('APP.SEARCH_RADIUS') + '' +
    'GROUP BY a.id, u.nickname ' +
    'ORDER BY a."beginAt" DESC ' +
    'LIMIT 10';

    return await models.sequelize.query(query, { type: models.sequelize.QueryTypes.SELECT });
};

/**
 * Create an attack.
 *
 * @param {Object} data
 * @returns {Promise.<void>}
 */
module.exports.create = async (data) => {
    await models.Attack.create({
        kaijus: data.kaijus,
        beginAt: data.beginAt,
        finishAt: data.finishAt,
        location: data.location,
        createdBy: data.createdBy
    });
};

/**
 * Delete an attack.
 *
 * @param {String} id
 * @returns {Promise.<void>}
 */
module.exports.delete = async (id) => {
    await models.Attack.destroy({where: {
        id: id
    }});
};

/**
 * Update an attack.
 *
 * @param {Object} data
 * @returns {Promise.<void>}
 */
module.exports.update = async (data) => {
    await models.Attack.update({
        kaijus: data.kaijus,
        beginAt: data.beginAt,
        finishAt: data.finishAt,
        location: data.location,
        createdBy: data.createdBy
    }, {
        where: {
            id: data.id
        }
    });
};
<|endoftext|>


<commit_before>import log from "../libs/log";
import multer from "multer";
import fs from "fs";
import * as PDFTOTEXT from "child_process";
import commandExists from "command-exists";

const UPLOAD = multer({dest: 'public/uploads/'}).single('pdf');

module.exports = app => {

	/**
	 * @api {post} /api/pdftotext pdf file to text/plain
	 * @apiParam {multipart/form-data} pdf pdf file
	 * @apiGroup Convert
	 * @apiSuccess {text/plain} body raw text
	 */
	app.post("/api/pdftotext", UPLOAD, (req, res) => {

		log.debug(req.headers);

		if (req.file.originalname.toLowerCase().indexOf(".pdf") === -1) {
			res.sendStatus(400);
			fs.unlink(req.file.path);
			return log.error("400 Error: File upload only supports .pdf filetype");
		}

		commandExists('pdftotext')
			.then(() => {
				PDFTOTEXT.exec("pdftotext -layout -nopgbrk -raw -eol unix " + req.file.path + " -", {maxBuffer: 3000 * 1024},
					(error, stdout, stderr) => {

						if (error) {
							fs.unlink(req.file.path);
							res.sendStatus(500);
							log.error(error);
							return log.error(stderr);
						}

						log.info(req.file.originalname + " Done!");
						log.debug("\n" + stdout);

						fs.unlink(req.file.path);
						res.type('text/plain');
						res.status(200).send(stdout.trim());
						res.flush();

					})
			}).catch(() => {
			fs.unlink(req.file.path);
			res.sendStatus(500);
			return log.error("500 Error: Command pdftotext doesn't exist");
		});
	});
};<commit_msg>update README.md<commit_after>import log from "../libs/log";
import multer from "multer";
import fs from "fs";
import * as PDFTOTEXT from "child_process";
import commandExists from "command-exists";

const UPLOAD = multer({dest: 'public/uploads/'}).single('pdf');

module.exports = app => {

	/**
	 * @api {post} /api/pdftotext pdf file to text/plain
	 * @apiParam {multipart/form-data} pdf pdf file
	 * @apiGroup Convert
	 * @apiSuccess {text/plain} body raw text
	 */
	app.post("/api/pdftotext", UPLOAD, (req, res) => {

		log.debug(req.headers);

		if (req.file.originalname.toLowerCase().indexOf(".pdf") === -1) {
			res.status(400).send("Wrong file type");
			fs.unlink(req.file.path);
			return log.error("400 Error: File upload only supports .pdf file type");
		}

		commandExists('pdftotext')
			.then(() => {
				PDFTOTEXT.exec("pdftotext -layout -nopgbrk -raw -eol unix " + req.file.path + " -", {maxBuffer: 3000 * 1024},
					(error, stdout, stderr) => {

						if (error) {
							fs.unlink(req.file.path);
							res.sendStatus(500);
							log.error(error);
							return log.error(stderr);
						}

						log.info(req.file.originalname + " Done!");
						log.debug("\n" + stdout);

						fs.unlink(req.file.path);
						res.type('text/plain');
						res.status(200).send(stdout.trim());
						res.flush();

					})
			}).catch(() => {
			fs.unlink(req.file.path);
			res.sendStatus(500);
			return log.error("500 Error: Command pdftotext doesn't exist");
		});
	});
};<|endoftext|>


<commit_before>
define([
  'jquery',
  'underscore',
  'backbone',
  'text!templates/topic_right_nav.html',
], function ($, _, Backbone, TopicRightNavTemplate) {
  return {

    initialize: function (title) {
      // unbind previous events
      $('#main').off();

      // unbind previous scroll event
      $(window).off('scroll');

      // remove binding of refresh button and hide by default
      $('#refresh-topic-container').off();

      // clear page content
      if (typeof (title) != undefined) {
        $('#main').html ('');
        this.setTitle (title);
      }

      this.loader(false);
    },

    setTitle: function (title, rightNavOptions) {
      $('#navbar-title').text (title);
      $('title').text (title);

      this.setRightNavbar (
        _.template (TopicRightNavTemplate, rightNavOptions));

      $('#refresh-topic').hide();
    },

    setRightNavbar: function (content) {
      $('ul.navbar-right').html (content);
    },

    bindRefresh: function (view) {
      $('#refresh-topic').show();
      $('#refresh-topic').click (function () {
        view.refresh ();
        return false;
      });
    },

    bindScroll: function(view) {
      $(window).scroll(function(ev) {
        view.checkScroll();
      });
    },

    checkScroll: function(view) {
      return $(window).scrollTop () + $(window).height () >
                 $("#main").height () - 200;
    },

    // loading animation
    // state is true if its loading, false if its not
    // need to wait before hiding animation
    loader_current_state: false,
    loader_current_timeout: -1,
    loader_show: function () {
      $('#loader').show();
    },
    loader_hide: function () {
      $('#loader').hide();
    },
    loader: function (state) {
      if (state == true && this.loader_current_state == false) {
        this.loader_show();
        this.loader_current_state = true;
      } else if (this.loader_current_state == true) {
        var that = this;
        if (this.loader_current_timeout != -1) {
          clearTimeout(this.loader_current_timeout);
        }

        this.loader_current_timeout = setTimeout(function () {
          that.loader_hide();
          that.loader_current_state = false;
          that.loader_current_timeout = -1;
        }, 1000);
      }
    }

  };
});
<commit_msg>scrollTop in initialization<commit_after>
define([
  'jquery',
  'underscore',
  'backbone',
  'text!templates/topic_right_nav.html',
], function ($, _, Backbone, TopicRightNavTemplate) {
  return {

    initialize: function (title) {
      // unbind previous events
      $('#main').off();

      // unbind previous scroll event
      $(window).off('scroll');

      // remove binding of refresh button and hide by default
      $('#refresh-topic-container').off();

      // scrollTop with initialize
      $(window).scrollTop();

      // clear page content
      if (typeof (title) != undefined) {
        $('#main').html ('');
        this.setTitle (title);
      }

      this.loader(false);
    },

    setTitle: function (title, rightNavOptions) {
      $('#navbar-title').text (title);
      $('title').text (title);

      this.setRightNavbar (
        _.template (TopicRightNavTemplate, rightNavOptions));

      $('#refresh-topic').hide();
    },

    setRightNavbar: function (content) {
      $('ul.navbar-right').html (content);
    },

    bindRefresh: function (view) {
      $('#refresh-topic').show();
      $('#refresh-topic').click (function () {
        view.refresh ();
        return false;
      });
    },

    bindScroll: function(view) {
      $(window).scroll(function(ev) {
        view.checkScroll();
      });
    },

    checkScroll: function(view) {
      return $(window).scrollTop () + $(window).height () >
                 $("#main").height () - 200;
    },

    // loading animation
    // state is true if its loading, false if its not
    // need to wait before hiding animation
    loader_current_state: false,
    loader_current_timeout: -1,
    loader_show: function () {
      $('#loader').show();
    },
    loader_hide: function () {
      $('#loader').hide();
    },
    loader: function (state) {
      if (state == true && this.loader_current_state == false) {
        this.loader_show();
        this.loader_current_state = true;
      } else if (this.loader_current_state == true) {
        var that = this;
        if (this.loader_current_timeout != -1) {
          clearTimeout(this.loader_current_timeout);
        }

        this.loader_current_timeout = setTimeout(function () {
          that.loader_hide();
          that.loader_current_state = false;
          that.loader_current_timeout = -1;
        }, 1000);
      }
    }

  };
});
<|endoftext|>


<commit_before>var PanelWidget = Widgetfly.Panel.extend({/** overwrite **/});
var ModalWidget = Widgetfly.Modal.extend({/** overwrite **/});

var pa0 = new PanelWidget({
	container : '.socialpanel',
	show : true,
	autoGrow : true,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/socialpanel/panel.html',
	options : {

	}
});

var pa1 = new PanelWidget({
	container : '.mypanel',
	show : true,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/panel.html',
	options : {
		hello : 'world'
	}
});
var m1 = new ModalWidget({
	show : false,
	backdrop : true,
	src : '../../../widgetfly.github.io/examples/widgets/filechooser/modal.html'
});

m1.on('selected', function(path){
	$('.file').html(path);
});

pa1.onStart(function() {
	console.log('p1 is started');
	pa1.trigger('hello', 'p1');

	pa1.onBeforeClose(function() {
		console.log('p1 is closing');
		return true;
	});

	pa1.on('openModalWidget', function() {
		m1.show();
	});

});

var PopoverWidget = Widgetfly.Popover.extend({/** overwrite **/});
var po1 = new PopoverWidget({
	target : '#target-right',
	placement : 'right',
	show : false,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/popover.html'
	//src : 'http://127.0.0.1:3000/widgetfly/example/dev/popover.html'
});

document.querySelector('#target-right').onclick = function() {
	po1.toggle();
};

var po2 = new PopoverWidget({
	target : '#target-left',
	placement : 'left',
	show : false,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/popover.html'
	//src : 'http://127.0.0.1:3000/widgetfly/example/dev/popover.html'
});

document.querySelector('#target-left').onclick = function() {
	po2.toggle();
};

var po3 = new PopoverWidget({
	target : '#target-top',
	placement : 'top',
	show : false,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/popover.html'
	//src : 'http://127.0.0.1:3000/widgetfly/example/dev/popover.html'
});

document.querySelector('#target-top').onclick = function() {
	po3.toggle();
};

var po4 = new PopoverWidget({
	target : '#target-bottom',
	placement : 'bottom',
	show : false,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/popover.html'
	//src : 'http://127.0.0.1:3000/widgetfly/example/dev/popover.html'
});

document.querySelector('#target-bottom').onclick = function() {
	po4.toggle();
};
<commit_msg>revert main.js<commit_after>var PanelWidget = Widgetfly.Panel.extend({/** overwrite **/});
var ModalWidget = Widgetfly.Modal.extend({/** overwrite **/});

var pa0 = new PanelWidget({
	container : '.socialpanel',
	show : true,
	autoGrow : true,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/socialpanel/panel.html',
	options : {

	}
});

var pa1 = new PanelWidget({
	container : '.mypanel',
	show : true,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/panel.html',
	options : {
		hello : 'world'
	}
});
var m1 = new ModalWidget({
	show : false,
	backdrop : true,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/filechooser/modal.html'
});

m1.on('selected', function(path){
	$('.file').html(path);
});

pa1.onStart(function() {
	console.log('p1 is started');
	pa1.trigger('hello', 'p1');

	pa1.onBeforeClose(function() {
		console.log('p1 is closing');
		return true;
	});

	pa1.on('openModalWidget', function() {
		m1.show();
	});

});

var PopoverWidget = Widgetfly.Popover.extend({/** overwrite **/});
var po1 = new PopoverWidget({
	target : '#target-right',
	placement : 'right',
	show : false,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/popover.html'
	//src : 'http://127.0.0.1:3000/widgetfly/example/dev/popover.html'
});

document.querySelector('#target-right').onclick = function() {
	po1.toggle();
};

var po2 = new PopoverWidget({
	target : '#target-left',
	placement : 'left',
	show : false,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/popover.html'
	//src : 'http://127.0.0.1:3000/widgetfly/example/dev/popover.html'
});

document.querySelector('#target-left').onclick = function() {
	po2.toggle();
};

var po3 = new PopoverWidget({
	target : '#target-top',
	placement : 'top',
	show : false,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/popover.html'
	//src : 'http://127.0.0.1:3000/widgetfly/example/dev/popover.html'
});

document.querySelector('#target-top').onclick = function() {
	po3.toggle();
};

var po4 = new PopoverWidget({
	target : '#target-bottom',
	placement : 'bottom',
	show : false,
	src : 'http://pqe.github.io/widgetfly/examples/widgets/popover.html'
	//src : 'http://127.0.0.1:3000/widgetfly/example/dev/popover.html'
});

document.querySelector('#target-bottom').onclick = function() {
	po4.toggle();
};
<|endoftext|>


<commit_before>var docker = require('docker-remote-api'),
  request = docker(),
  Q = require('q');

var DockerRemote = function() {};

DockerRemote.containers = function() {
  var defer = Q.defer();
  request.get('/containers/json', {json:true}, function(err, containers) {
    if (err) defer.reject(err);
    defer.resolve(containers);
  });
  return defer.promise;
};

DockerRemote.processes = function(containerID) {
  var defer = Q.defer();
  request.get('/containers/'+ containerID +'/top', {json:true}, function(err, processes) {
    if (err) defer.reject(err);
    defer.resolve(processes);
  });
  return defer.promise;
};

DockerRemote.stats = function(containerID) {
  var defer = Q.defer();
  request.get('/containers/'+ containerID +'/stats?stream=false', {json:true}, function(err, stats) {
    if (err) defer.reject(err);
    defer.resolve(stats);
  });
  return defer.promise;
};

module.exports = DockerRemote;
<commit_msg>Remove defers from docker remote promise api<commit_after>var docker = require('docker-remote-api'),
  request = docker(),
  Q = require('q');

var DockerRemote = function() {};

DockerRemote.containers = function() {
  return new Q.Promise(function(resolve, reject) {
    request.get('/containers/json', {json:true}, function(err, containers) {
      if (err) defer.reject(err);
      resolve(containers);
    });
  });
};

DockerRemote.processes = function(containerID) {
  return new Q.Promise(function(resolve, reject) {
    request.get('/containers/'+ containerID +'/top', {json:true}, function(err, processes) {
      if (err) defer.reject(err);
      resolve(processes);
    });
  });
};

DockerRemote.stats = function(containerID) {
  return new Q.Promise(function(resolve, reject) {
    request.get('/containers/'+ containerID +'/stats?stream=false', {json:true}, function(err, stats) {
      if (err) defer.reject(err);
      resolve(stats);
    });
  });
};

module.exports = DockerRemote;
<|endoftext|>


<commit_before>
/**
 * Test dependencies.
 */

var Node = require('../lib/node');
var assert = require('assert');

describe("node", function() {

  it('should create node with type', function() {
    var node = new Node(1);
    assert.equal(node.nodeType, 1);
  });

  describe('append node', function() {

    it('should append an other node', function() {
      var node = new Node(1);
      node.appendChild(new Node(3));

      assert.equal(node.childNodes.length, 1);
      assert.equal(node.childNodes[0].nodeType, 3);
    });

    it('shoud set parent node', function() {
      var node = new Node(1);
      var child = node.appendChild(new Node(3));

      assert.deepEqual(node, child.parentNode);
    });

  });

  describe("child nodes", function() {

    it("should return first child", function() {
      var node = new Node(1);
      var child = node.appendChild(new Node(3));

      assert.deepEqual(node.firstChild, child);
    });

    it('should return next sibling', function() {
      var node = new Node(1);
      var child = node.appendChild(new Node(3));
      var sibling = node.appendChild(new Node(3));

      assert.deepEqual(node.firstChild.nextSibling, sibling);
    });

  });

});
<commit_msg>start implementing replaceChild<commit_after>
/**
 * Test dependencies.
 */

var Node = require('../lib/node');
var assert = require('assert');

describe("node", function() {

  it('should create node with type', function() {
    var node = new Node(1);
    assert.equal(node.nodeType, 1);
  });

  describe('append node', function() {

    it('should append an other node', function() {
      var node = new Node(1);
      node.appendChild(new Node(3));

      assert.equal(node.childNodes.length, 1);
      assert.equal(node.childNodes[0].nodeType, 3);
    });

    it('shoud set parent node', function() {
      var node = new Node(1);
      var child = node.appendChild(new Node(3));

      assert.deepEqual(node, child.parentNode);
    });

  });

  describe("child nodes", function() {

    it("should return first child", function() {
      var node = new Node(1);
      var child = node.appendChild(new Node(3));

      assert.deepEqual(node.firstChild, child);
    });

    it('should return next sibling', function() {
      var node = new Node(1);
      var child = node.appendChild(new Node(3));
      var sibling = node.appendChild(new Node(3));

      assert.deepEqual(node.firstChild.nextSibling, sibling);
    });

    describe("replace child", function() {

      var node, first, second;
      beforeEach(function() {
        node = new Node(1);
        first = node.appendChild(new Node(3));
        second = node.appendChild(new Node(3));
      });

      it('should replace node', function() {
        var third = new Node(3);
        node.replaceChild(third, first);

        assert.equal(node.childNodes[0], third);
      });

    });

  });

});
<|endoftext|>


<commit_before>'use strict';

let debug = require('debug')('rss:ptt:index');
let express = require('express');
let NodeCache = require('node-cache');
let Promise = require('bluebird');
let RSS = require('rss');
let router = express.Router();
let cache = new NodeCache({ stdTTL: 60 * 5, checkperiod: 0 });
let articleCache = new NodeCache({ stdTTL: 60 * 60, checkperiod: 0 });
let getArticlesFromLink = require('ptt').getArticlesFromLink;
let getArticleFromLink = require('ptt').getArticleFromLink;

function matchTitle(article, keywords) {
  for (var index = 0; index < keywords.length; index++) {
    if (article.title.indexOf(keywords[index]) !== -1) {
      debug('title: %s matched keyword: %s', article.title, keywords[index]);
      return true;
    }
  }

  debug('title: %s not matched any keywords: %s', article.title, keywords);
  return false;
}

function filterArticles(articles, keywords, exclude=false) {
  return articles.filter((article) => exclude ^ matchTitle(article, keywords));
}

function generateRSS(data, fetchContent) {
  fetchContent = (fetchContent === true);
  let articles = data.articles;
  let feed = new RSS({
    title: data.board,
    description: 'PTT: ' + data.board,
    link: 'https://www.ptt.cc',
    site_url: data.siteUrl,
    generator: 'PttRSS',
    pubDate: new Date(),
  });

  // filter by title keywords
  if (data.titleKeywords && data.titleKeywords.length > 0) {
    articles = filterArticles(data.articles, data.titleKeywords);
  }

  if (data.exTitleKeywords && data.exTitleKeywords.length > 0) {
    debug(data.exTitleKeywords);
    articles = filterArticles(data.articles, data.exTitleKeywords, true);
  }

  // filter by push counts
  articles = articles.filter(article => article.push > data.push);

  if (fetchContent === false) {
    return new Promise((resolve, reject) => {
      articles.forEach((articleMeta) => {
        feed.item(articleMeta);
      });
      resolve(feed);
    });
  }

  return Promise.map(articles, (articleMeta) => {
    let article = articleCache.get(articleMeta.url);
    if (article) {
      debug('cached article: %s', article.title);
      feed.item(article);
      return;
    }

    return getArticleFromLink(articleMeta.url)
      .then(article => {
        article = Object.assign(articleMeta, article);
        feed.item(article);
        debug('set cache article: %s', article.title, article.url);
        articleCache.set(article.url, article);
        return;
      })
      .delay(100);

  }, { concurrency: 3 }).then(() => Promise.resolve(feed));
}

router
  .get('/:board\.xml', (req, res, next) => {
    if (!req.params.board) return next(Error('Invaild Parameters'));

    const board = req.params.board.toLowerCase();
    const siteUrl = 'https://www.ptt.cc/bbs/' + board + '/index.html';
    const push = req.query.push || -99;
    const minArticleCount = req.query.minArticleCount || 50;
    const cachedKey = req.originalUrl;
    const fetchContent = req.query.fetchContent === 'true';
    let titleKeywords = req.query.title || [];
    if (!Array.isArray(titleKeywords)) {
      titleKeywords = [titleKeywords];
    }

    let exTitleKeywords = req.query.extitle || [];
    if (!Array.isArray(exTitleKeywords)) {
      exTitleKeywords = [exTitleKeywords];
    }

    // Get from cache first
    const obj = cache.get(cachedKey);
    if (obj) {
      return generateRSS({
        siteUrl: siteUrl,
        board: board,
        articles: obj.articles,
        titleKeywords,
        exTitleKeywords,
        push: push,
      }, fetchContent)
      .then((feed) => {
        debug('cached board: %s', board, cachedKey);
        res.set('Content-Type', 'text/xml');
        return res.send(feed.xml());
      })
      .catch((err) => next(err));
    }

    let response = function response(articles) {
      debug('set cache board: %s', board, cachedKey);
      cache.set(
        cachedKey,
        { articles: articles }
      );

      return generateRSS({
        siteUrl,
        board,
        articles,
        titleKeywords,
        exTitleKeywords,
        push,
      }, fetchContent);
    };

    let articles = [];
    let getArticles = function (data) {
      if (!data.articles) throw Error('Fetch failed');

      articles = articles.concat(data.articles);
      if (articles.length < minArticleCount) {
        debug('get more articles, current count: %s', articles.length);
        return getArticlesFromLink(data.nextPageUrl)
          .then(data => getArticles(data));
      }

      return Promise.resolve(articles);
    };

    // Start crawling board index
    getArticlesFromLink(siteUrl)
      .then(data => getArticles(data))
      .then(articles => response(articles))
      .then(feed => {
        res.set('Content-Type', 'text/xml');
        res.send(feed.xml());
        return;
      })
      .catch(err => next(err));
  });

module.exports = {
  router,
};
<commit_msg>title match change to case insensitive<commit_after>'use strict';

let debug = require('debug')('rss:ptt:index');
let express = require('express');
let NodeCache = require('node-cache');
let Promise = require('bluebird');
let RSS = require('rss');
let router = express.Router();
let cache = new NodeCache({ stdTTL: 60 * 5, checkperiod: 0 });
let articleCache = new NodeCache({ stdTTL: 60 * 60, checkperiod: 0 });
let getArticlesFromLink = require('ptt').getArticlesFromLink;
let getArticleFromLink = require('ptt').getArticleFromLink;

function matchTitle(article, keywords) {
  for (var index = 0; index < keywords.length; index++) {
    if (article.title.toLowerCase().indexOf(
        keywords[index].toLowerCase()) !== -1) {
      debug('title: %s matched keyword: %s', article.title, keywords[index]);
      return true;
    }
  }

  debug('title: %s not matched any keywords: %s', article.title, keywords);
  return false;
}

function filterArticles(articles, keywords, exclude=false) {
  return articles.filter((article) => exclude ^ matchTitle(article, keywords));
}

function generateRSS(data, fetchContent) {
  fetchContent = (fetchContent === true);
  let articles = data.articles;
  let feed = new RSS({
    title: data.board,
    description: 'PTT: ' + data.board,
    link: 'https://www.ptt.cc',
    site_url: data.siteUrl,
    generator: 'PttRSS',
    pubDate: new Date(),
  });

  // filter by title keywords
  if (data.titleKeywords && data.titleKeywords.length > 0) {
    articles = filterArticles(data.articles, data.titleKeywords);
  }

  if (data.exTitleKeywords && data.exTitleKeywords.length > 0) {
    debug(data.exTitleKeywords);
    articles = filterArticles(data.articles, data.exTitleKeywords, true);
  }

  // filter by push counts
  articles = articles.filter(article => article.push > data.push);

  if (fetchContent === false) {
    return new Promise((resolve, reject) => {
      articles.forEach((articleMeta) => {
        feed.item(articleMeta);
      });
      resolve(feed);
    });
  }

  return Promise.map(articles, (articleMeta) => {
    let article = articleCache.get(articleMeta.url);
    if (article) {
      debug('cached article: %s', article.title);
      feed.item(article);
      return;
    }

    return getArticleFromLink(articleMeta.url)
      .then(article => {
        article = Object.assign(articleMeta, article);
        feed.item(article);
        debug('set cache article: %s', article.title, article.url);
        articleCache.set(article.url, article);
        return;
      })
      .delay(100);

  }, { concurrency: 3 }).then(() => Promise.resolve(feed));
}

router
  .get('/:board\.xml', (req, res, next) => {
    if (!req.params.board) return next(Error('Invaild Parameters'));

    const board = req.params.board.toLowerCase();
    const siteUrl = 'https://www.ptt.cc/bbs/' + board + '/index.html';
    const push = req.query.push || -99;
    const minArticleCount = req.query.minArticleCount || 50;
    const cachedKey = req.originalUrl;
    const fetchContent = req.query.fetchContent === 'true';
    let titleKeywords = req.query.title || [];
    if (!Array.isArray(titleKeywords)) {
      titleKeywords = [titleKeywords];
    }

    let exTitleKeywords = req.query.extitle || [];
    if (!Array.isArray(exTitleKeywords)) {
      exTitleKeywords = [exTitleKeywords];
    }

    // Get from cache first
    const obj = cache.get(cachedKey);
    if (obj) {
      return generateRSS({
        siteUrl: siteUrl,
        board: board,
        articles: obj.articles,
        titleKeywords,
        exTitleKeywords,
        push: push,
      }, fetchContent)
      .then((feed) => {
        debug('cached board: %s', board, cachedKey);
        res.set('Content-Type', 'text/xml');
        return res.send(feed.xml());
      })
      .catch((err) => next(err));
    }

    let response = function response(articles) {
      debug('set cache board: %s', board, cachedKey);
      cache.set(
        cachedKey,
        { articles: articles }
      );

      return generateRSS({
        siteUrl,
        board,
        articles,
        titleKeywords,
        exTitleKeywords,
        push,
      }, fetchContent);
    };

    let articles = [];
    let getArticles = function (data) {
      if (!data.articles) throw Error('Fetch failed');

      articles = articles.concat(data.articles);
      if (articles.length < minArticleCount) {
        debug('get more articles, current count: %s', articles.length);
        return getArticlesFromLink(data.nextPageUrl)
          .then(data => getArticles(data));
      }

      return Promise.resolve(articles);
    };

    // Start crawling board index
    getArticlesFromLink(siteUrl)
      .then(data => getArticles(data))
      .then(articles => response(articles))
      .then(feed => {
        res.set('Content-Type', 'text/xml');
        res.send(feed.xml());
        return;
      })
      .catch(err => next(err));
  });

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


<commit_before>// Magic scroll things.
// This binds cool animations to the scroll level in such a way that the
// animations' progress can be controlled by scrolling. This does what
// ScrollMagic does except without the bugs.

const elem = {
  body: document.body,

  header: document.getElementsByTagName('header')[0],
  headerTitle: document.getElementsByTagName('h1')[0],
  downIndicator: document.getElementsByClassName('down-indicator')[0],

  laptop: document.getElementsByClassName('laptop')[0],
  laptopContent: document.getElementsByClassName('laptop-content')[0],
  laptopBase: document.getElementsByClassName('laptop-base')[0],

  laptopsContainer: document.getElementsByClassName('laptops-container')[0],
  laptopsStretcher: document.getElementsByClassName('laptops-stretcher')[0],
};

// HEADER TRANSITION LOGIC =========================================================================

function setHeaderElementsPinned(fixed) {
  // Switch between fixed and absolute, pinning elements in the appropriate place
  [elem.headerTitle, elem.laptop].forEach((element) => {
    /* eslint-disable no-param-reassign */
    element.style.position = fixed ? 'fixed' : 'absolute';
    element.style.top = fixed ? '50vh' : `${window.innerHeight}px`;
    /* eslint-enable no-param-reassign */
  });
}

function updateHeaderElements(progress) {
  // Blends two values based on "progress" made between them.
  function tween(a, b) {
    return a + ((b - a) * progress);
  }

  // FIT MAIN HEADER TO LAPTOP SCREEN

  // Beginning and end positions
  const w = window.innerWidth; const h = window.innerHeight;
  const posA = { left: 0, top: 0, right: w, bottom: h, width: w, height: h };
  const posB = elem.laptopContent.getBoundingClientRect();

  // Calculate translation
  // Center is used for calculating translation because element is scaled about the center)
  const centerA = { x: (posA.left + posA.right) / 2, y: (posA.top + posA.bottom) / 2 };
  const centerB = { x: (posB.left + posB.right) / 2, y: (posB.top + posB.bottom) / 2 };
  const translationNeeded = { x: centerB.x - centerA.x, y: centerB.y - centerA.y };

  // Calculate scale
  const scaleNeeded = {
    x: posB.width / posA.width,
    y: posB.height / posA.height,
  };

  // Apply transformations
  const transform = [];
  // Translations
  transform.push(`translateX(${tween(0, translationNeeded.x)}px)`);
  transform.push(`translateY(${tween(0, translationNeeded.y)}px)`);
  // Scale
  transform.push(`scaleX(${tween(1, scaleNeeded.x)})`);
  transform.push(`scaleY(${tween(1, scaleNeeded.y)})`);
  elem.header.style.transform = transform.join(' ');

  // MISCELLANEOUS

  // Update opacity of down indicator
  elem.downIndicator.style.opacity = 1 - progress;
  // Reduce font size of my name
  elem.headerTitle.style.transform = `translate(-50%, -50%) scale(${tween(1, 0.7)})`;

  // ADJUST CANVAS SETTINGS

  const canvasScale = ( // Mean of X scale difference and Y scale difference
    (window.gol.elem.offsetWidth / window.innerWidth) +
    (window.gol.elem.offsetHeight / window.innerHeight)
  ) / 2;
  window.gol.idealCellSize = 20 * canvasScale;
  window.gol.needsSizeUpdate = true;
}

// BACKGROUND TRANSITION LOGIC =====================================================================

function updateBackgroundColor(progress) {
  const grad1A = window.parseColor('#5EFCE8');
  const grad1B = window.parseColor('#736EFE');

  const grad2A = window.parseColor('#16222A');
  const grad2B = window.parseColor('#4A5070');

  const interpolate = (a, b, amount) => ({
    r: Math.floor((a.r * (1 - progress)) + (b.r * amount)),
    g: Math.floor((a.g * (1 - progress)) + (b.g * amount)),
    b: Math.floor((a.b * (1 - progress)) + (b.b * amount)),
  });

  const getCssGradString = (a, b, deg = 135) =>
    `linear-gradient(${deg}deg, rgb(${a.r}, ${a.g}, ${a.b}), rgb(${b.r}, ${b.g}, ${b.b}))`;

  const calcA = interpolate(grad1A, grad2A, progress);
  const calcB = interpolate(grad1B, grad2B, progress);
  elem.body.style.backgroundImage = getCssGradString(calcA, calcB);
}

// SCROLL LISTENER =================================================================================

function onscroll() {
  const scroll = window.scrollY;

  // Header

  switch (true) {
    case (scroll < 5): {
      setHeaderElementsPinned(true);
      requestAnimationFrame(() => updateHeaderElements(0));
      break;
    }

    case (scroll < window.innerHeight / 2): {
      setHeaderElementsPinned(true);
      const headerProgress = scroll / (window.innerHeight / 2);
      requestAnimationFrame(() => updateHeaderElements(headerProgress));
      break;
    }

    default: {
      setHeaderElementsPinned(false);
      requestAnimationFrame(() => updateHeaderElements(1));
    }
  }

  // Background

  switch (true) {
    case (scroll < window.innerHeight * 0.75): {
      updateBackgroundColor(0);
      break;
    }

    case (scroll > window.innerHeight * 1.25): {
      updateBackgroundColor(1);
      break;
    }

    default: {
      const bgProgress = (scroll - (window.innerHeight * 0.75)) / (window.innerHeight * 0.5);
      updateBackgroundColor(bgProgress);
    }
  }

  // Carousel

  const largerScreenDimension = Math.max(window.innerWidth, window.innerHeight);

  // Adjust carousel position
  const start = elem.laptopsStretcher.offsetTop;
  const end = start + (largerScreenDimension * 1.5);

  const carouselProgress = (scroll - start) / (end - start);
  requestAnimationFrame(() => {
    window.carousel.position = carouselProgress * window.carousel.maxIndex;
  });
}

window.onscroll = onscroll;

document.addEventListener('scroll', onscroll);
window.addEventListener('resize', onscroll);
<commit_msg>Shorten further<commit_after>// Magic scroll things.
// This binds cool animations to the scroll level in such a way that the
// animations' progress can be controlled by scrolling. This does what
// ScrollMagic does except without the bugs.

const elem = {
  body: document.body,

  header: document.getElementsByTagName('header')[0],
  headerTitle: document.getElementsByTagName('h1')[0],
  downIndicator: document.getElementsByClassName('down-indicator')[0],

  laptop: document.getElementsByClassName('laptop')[0],
  laptopContent: document.getElementsByClassName('laptop-content')[0],
  laptopBase: document.getElementsByClassName('laptop-base')[0],

  laptopsContainer: document.getElementsByClassName('laptops-container')[0],
  laptopsStretcher: document.getElementsByClassName('laptops-stretcher')[0],
};

// HEADER TRANSITION LOGIC =========================================================================

function setHeaderElementsPinned(fixed) {
  // Switch between fixed and absolute, pinning elements in the appropriate place
  [elem.headerTitle, elem.laptop].forEach((element) => {
    /* eslint-disable no-param-reassign */
    element.style.position = fixed ? 'fixed' : 'absolute';
    element.style.top = fixed ? '50vh' : `${window.innerHeight}px`;
    /* eslint-enable no-param-reassign */
  });
}

function updateHeaderElements(progress) {
  // Blends two values based on "progress" made between them.
  function tween(a, b) {
    return a + ((b - a) * progress);
  }

  // FIT MAIN HEADER TO LAPTOP SCREEN

  // Beginning and end positions
  const w = window.innerWidth; const h = window.innerHeight;
  const posA = { left: 0, top: 0, right: w, bottom: h, width: w, height: h };
  const posB = elem.laptopContent.getBoundingClientRect();
  // Calculate translation
  // Center is used for calculating translation because element is scaled about the center)
  const centerA = { x: (posA.left + posA.right) / 2, y: (posA.top + posA.bottom) / 2 };
  const centerB = { x: (posB.left + posB.right) / 2, y: (posB.top + posB.bottom) / 2 };
  const translationNeeded = { x: centerB.x - centerA.x, y: centerB.y - centerA.y };
  // Calculate scale
  const scaleNeeded = { x: posB.width / posA.width, y: posB.height / posA.height };
  // Apply transformations
  elem.header.style.transform = [
    // Translate
    `translateX(${tween(0, translationNeeded.x)}px)`,
    `translateY(${tween(0, translationNeeded.y)}px)`,
    // Scale
    `scaleX(${tween(1, scaleNeeded.x)})`,
    `scaleY(${tween(1, scaleNeeded.y)})`,
  ].join(' ');

  // MISCELLANEOUS

  // Update opacity of down indicator
  elem.downIndicator.style.opacity = 1 - progress;
  // Reduce font size of my name
  elem.headerTitle.style.transform = `translate(-50%, -50%) scale(${tween(1, 0.7)})`;

  // ADJUST CANVAS SETTINGS

  const canvasScale = ( // Mean of X scale difference and Y scale difference
    (window.gol.elem.offsetWidth / window.innerWidth) +
    (window.gol.elem.offsetHeight / window.innerHeight)
  ) / 2;
  window.gol.idealCellSize = 20 * canvasScale;
  window.gol.needsSizeUpdate = true;
}

// BACKGROUND TRANSITION LOGIC =====================================================================

function updateBackgroundColor(progress) {
  const grad1A = window.parseColor('#5EFCE8');
  const grad1B = window.parseColor('#736EFE');

  const grad2A = window.parseColor('#16222A');
  const grad2B = window.parseColor('#4A5070');

  const interpolate = (a, b, amount) => ({
    r: Math.floor((a.r * (1 - progress)) + (b.r * amount)),
    g: Math.floor((a.g * (1 - progress)) + (b.g * amount)),
    b: Math.floor((a.b * (1 - progress)) + (b.b * amount)),
  });

  const getCssGradString = (a, b, deg = 135) =>
    `linear-gradient(${deg}deg, rgb(${a.r}, ${a.g}, ${a.b}), rgb(${b.r}, ${b.g}, ${b.b}))`;

  const calcA = interpolate(grad1A, grad2A, progress);
  const calcB = interpolate(grad1B, grad2B, progress);
  elem.body.style.backgroundImage = getCssGradString(calcA, calcB);
}

// SCROLL LISTENER =================================================================================

function onscroll() {
  const scroll = window.scrollY;

  // Header

  switch (true) {
    case (scroll < 5): {
      setHeaderElementsPinned(true);
      requestAnimationFrame(() => updateHeaderElements(0));
      break;
    }

    case (scroll < window.innerHeight / 2): {
      setHeaderElementsPinned(true);
      const headerProgress = scroll / (window.innerHeight / 2);
      requestAnimationFrame(() => updateHeaderElements(headerProgress));
      break;
    }

    default: {
      setHeaderElementsPinned(false);
      requestAnimationFrame(() => updateHeaderElements(1));
    }
  }

  // Background

  switch (true) {
    case (scroll < window.innerHeight * 0.75): {
      updateBackgroundColor(0);
      break;
    }

    case (scroll > window.innerHeight * 1.25): {
      updateBackgroundColor(1);
      break;
    }

    default: {
      const bgProgress = (scroll - (window.innerHeight * 0.75)) / (window.innerHeight * 0.5);
      updateBackgroundColor(bgProgress);
    }
  }

  // Carousel

  const largerScreenDimension = Math.max(window.innerWidth, window.innerHeight);

  // Adjust carousel position
  const start = elem.laptopsStretcher.offsetTop;
  const end = start + (largerScreenDimension * 1.5);

  const carouselProgress = (scroll - start) / (end - start);
  requestAnimationFrame(() => {
    window.carousel.position = carouselProgress * window.carousel.maxIndex;
  });
}

window.onscroll = onscroll;

document.addEventListener('scroll', onscroll);
window.addEventListener('resize', onscroll);
<|endoftext|>