<commit_before>'use strict';

/**
 * Module dependencies
 */
import * as mongo from 'mongodb';
import parse from 'misskey-text';
import Post from '../../models/post';
import User from '../../models/user';
import Following from '../../models/following';
import DriveFile from '../../models/drive-file';
import serialize from '../../serializers/post';
//import extractHashtags from '../../core/extract-hashtags';
//import registerHashtags from '../../core/register-hashtags';
import createFile from '../../common/add-file-to-drive';
import notify from '../../common/notify';
import event from '../../event';
import es from '../../db/elasticsearch';

/**
 * 最大文字数
 */
const maxTextLength = 300;

/**
 * 添付できるファイルの数
 */
const maxFileLength = 4;

/**
 * Create a post
 *
 * @param {Object} params
 * @param {Object} user
 * @param {Object} app
 * @return {Promise<object>}
 */
module.exports = (params, user, app) =>
	new Promise(async (res, rej) =>
{
	// Get 'text' parameter
	let text = params.text;
	if (text !== undefined && text !== null) {
		text = text.trim();
		if (text.length === 0) {
			text = null;
		} else if (text.length > maxTextLength) {
			return rej('too long text');
		}
	} else {
		text = null;
	}

	// Get 'images' parameter
	let images = params.images;
	let files = [];
	if (images !== undefined && images !== null) {
		images = images.split(',');

		if (images.length > maxFileLength) {
			return rej('too many images');
		}

		// Drop duplicates
		images = images.filter((x, i, self) => self.indexOf(x) === i);

		// Fetch files
		for (let i = 0; i < images.length; i++) {
			const image = images[i];

			const entity = await DriveFile.findOne({
				_id: new mongo.ObjectID(image),
				user: user._id
			}, {
				data: false
			});

			if (entity === null) {
				return rej('file not found');
			} else {
				files.push(entity);
			}
		}
	} else {
		files = null;
	}

	// Get 'repost' parameter
	let repost = params.repost;
	if (repost !== undefined && repost !== null) {
		// Get repostee
		repost = await Post.findOne({
			_id: new mongo.ObjectID(repost)
		});

		if (repost === null) {
			return rej('repostee is not found');
		} else if (repost.repost && !repost.text && !repost.images) {
			return rej('cannot repost from repost');
		}

		// Get recently post
		const latestPost = await Post.findOne({
			user: user._id
		}, {}, {
			sort: {
				_id: -1
			}
		});

		// 直近と同じRepost対象かつ引用じゃなかったらエラー
		if (latestPost &&
				latestPost.repost &&
				latestPost.repost.toString() === repost._id.toString() &&
				text === null && files === null) {
			return rej('二重Repostです(NEED TRANSLATE)');
		}

		// 直近がRepost対象かつ引用じゃなかったらエラー
		if (latestPost &&
				latestPost._id.toString() === repost._id.toString() &&
				text === null && files === null) {
			return rej('二重Repostです(NEED TRANSLATE)');
		}
	} else {
		repost = null;
	}

	// Get 'reply_to' parameter
	let replyTo = params.reply_to;
	if (replyTo !== undefined && replyTo !== null) {
		replyTo = await Post.findOne({
			_id: new mongo.ObjectID(replyTo)
		});

		if (replyTo === null) {
			return rej('reply to post is not found');
		}

		// 返信対象が引用でないRepostだったらエラー
		if (replyTo.repost && !replyTo.text && !replyTo.images) {
			return rej('cannot reply to repost');
		}
	} else {
		replyTo = null;
	}

	// テキストが無いかつ添付ファイルが無いかつRepostも無かったらエラー
	if (text === null && files === null && repost === null) {
		return rej('text, images or repost is required');
	}

	// 投稿を作成
	const inserted = await Post.insert({
		created_at: new Date(),
		images: images ? files.map(file => file._id) : undefined,
		reply_to: replyTo ? replyTo._id : undefined,
		repost: repost ? repost._id : undefined,
		text: text,
		user: user._id,
		app: app ? app._id : null
	});

	const post = inserted.ops[0];

	// Serialize
	const postObj = await serialize(post);

	// Reponse
	res(postObj);

	// 自分のストリーム
	event(user._id, 'post', postObj);

	const followers = await Following
		.find({ followee: user._id }, {
			follower: true,
			_id: false
		})
		.toArray();

	// 自分のフォロワーのストリーム
	followers.forEach(following => {
		event(following.follower, 'post', postObj);
	});

	// Increment my posts count
	User.updateOne({ _id: user._id }, {
		$inc: {
			posts_count: 1
		}
	});

	// Update replyee status
	if (replyTo) {
		Post.updateOne({ _id: replyTo._id }, {
			$inc: {
				replies_count: 1
			}
		});
	}

	if (repost) {
		// Publish event
		event(repost.user, 'repost', postObj);

		// Notify
		if (repost.user.toString() !== user._id.toString()) {
			notify(repost.user, 'repost', {
				post: post._id
			});
		}

		// 今までで同じ投稿をRepostしているか
		const existRepost = await Post.findOne({
			user: user._id,
			repost: repost._id,
			_id: {
				$ne: post._id
			}
		});

		if (existRepost === null) {
			// Update repostee status
			Post.updateOne({ _id: repost._id }, {
				$inc: {
					repost_count: 1
				}
			});
		}
	}

	// Register to search database
	if (text != null) {
		es.index({
			index: 'misskey',
			type: 'post',
			id: post._id.toString(),
			body: {
				text: post.text
			}
		});

		const tokens = parse(text);

		// Extract a mentions
		const mentions = tokens
			.filter(t => t.type == 'mention')
			.map(m => m.username);

		mentions.forEach(async (mention) => {
			// Fetch mentioned user
			const mentionedUser = await User
				.findOne({ username_lower: mention.toLowerCase() });

			// Notify
			if (mentionedUser._id.toString() !== user._id.toString()) {
				notify(mentionedUser._id, 'mention', {
					post: post._id
				});
			}
		});

		// ハッシュタグ抽出
		//const hashtags = extractHashtags(text);

		// ハッシュタグをデータベースに登録
		//registerHashtags(user, hashtags);
	}
});
<commit_msg>Add note<commit_after>'use strict';

/**
 * Module dependencies
 */
import * as mongo from 'mongodb';
import parse from 'misskey-text';
import Post from '../../models/post';
import User from '../../models/user';
import Following from '../../models/following';
import DriveFile from '../../models/drive-file';
import serialize from '../../serializers/post';
//import extractHashtags from '../../core/extract-hashtags';
//import registerHashtags from '../../core/register-hashtags';
import createFile from '../../common/add-file-to-drive';
import notify from '../../common/notify';
import event from '../../event';
import es from '../../db/elasticsearch';

/**
 * 最大文字数
 */
const maxTextLength = 300;

/**
 * 添付できるファイルの数
 */
const maxFileLength = 4;

/**
 * Create a post
 *
 * @param {Object} params
 * @param {Object} user
 * @param {Object} app
 * @return {Promise<object>}
 */
module.exports = (params, user, app) =>
	new Promise(async (res, rej) =>
{
	// Get 'text' parameter
	let text = params.text;
	if (text !== undefined && text !== null) {
		text = text.trim();
		if (text.length === 0) {
			text = null;
		} else if (text.length > maxTextLength) {
			return rej('too long text');
		}
	} else {
		text = null;
	}

	// Get 'images' parameter
	let images = params.images;
	let files = [];
	if (images !== undefined && images !== null) {
		images = images.split(',');

		if (images.length > maxFileLength) {
			return rej('too many images');
		}

		// Drop duplicates
		images = images.filter((x, i, self) => self.indexOf(x) === i);

		// Fetch files
		// forEach だと途中でエラーなどがあっても return できないので
		// 敢えて for を使っています。
		for (let i = 0; i < images.length; i++) {
			const image = images[i];

			const entity = await DriveFile.findOne({
				_id: new mongo.ObjectID(image),
				user: user._id
			}, {
				data: false
			});

			if (entity === null) {
				return rej('file not found');
			} else {
				files.push(entity);
			}
		}
	} else {
		files = null;
	}

	// Get 'repost' parameter
	let repost = params.repost;
	if (repost !== undefined && repost !== null) {
		// Get repostee
		repost = await Post.findOne({
			_id: new mongo.ObjectID(repost)
		});

		if (repost === null) {
			return rej('repostee is not found');
		} else if (repost.repost && !repost.text && !repost.images) {
			return rej('cannot repost from repost');
		}

		// Get recently post
		const latestPost = await Post.findOne({
			user: user._id
		}, {}, {
			sort: {
				_id: -1
			}
		});

		// 直近と同じRepost対象かつ引用じゃなかったらエラー
		if (latestPost &&
				latestPost.repost &&
				latestPost.repost.toString() === repost._id.toString() &&
				text === null && files === null) {
			return rej('二重Repostです(NEED TRANSLATE)');
		}

		// 直近がRepost対象かつ引用じゃなかったらエラー
		if (latestPost &&
				latestPost._id.toString() === repost._id.toString() &&
				text === null && files === null) {
			return rej('二重Repostです(NEED TRANSLATE)');
		}
	} else {
		repost = null;
	}

	// Get 'reply_to' parameter
	let replyTo = params.reply_to;
	if (replyTo !== undefined && replyTo !== null) {
		replyTo = await Post.findOne({
			_id: new mongo.ObjectID(replyTo)
		});

		if (replyTo === null) {
			return rej('reply to post is not found');
		}

		// 返信対象が引用でないRepostだったらエラー
		if (replyTo.repost && !replyTo.text && !replyTo.images) {
			return rej('cannot reply to repost');
		}
	} else {
		replyTo = null;
	}

	// テキストが無いかつ添付ファイルが無いかつRepostも無かったらエラー
	if (text === null && files === null && repost === null) {
		return rej('text, images or repost is required');
	}

	// 投稿を作成
	const inserted = await Post.insert({
		created_at: new Date(),
		images: images ? files.map(file => file._id) : undefined,
		reply_to: replyTo ? replyTo._id : undefined,
		repost: repost ? repost._id : undefined,
		text: text,
		user: user._id,
		app: app ? app._id : null
	});

	const post = inserted.ops[0];

	// Serialize
	const postObj = await serialize(post);

	// Reponse
	res(postObj);

	// 自分のストリーム
	event(user._id, 'post', postObj);

	const followers = await Following
		.find({ followee: user._id }, {
			follower: true,
			_id: false
		})
		.toArray();

	// 自分のフォロワーのストリーム
	followers.forEach(following => {
		event(following.follower, 'post', postObj);
	});

	// Increment my posts count
	User.updateOne({ _id: user._id }, {
		$inc: {
			posts_count: 1
		}
	});

	// Update replyee status
	if (replyTo) {
		Post.updateOne({ _id: replyTo._id }, {
			$inc: {
				replies_count: 1
			}
		});
	}

	if (repost) {
		// Publish event
		event(repost.user, 'repost', postObj);

		// Notify
		if (repost.user.toString() !== user._id.toString()) {
			notify(repost.user, 'repost', {
				post: post._id
			});
		}

		// 今までで同じ投稿をRepostしているか
		const existRepost = await Post.findOne({
			user: user._id,
			repost: repost._id,
			_id: {
				$ne: post._id
			}
		});

		if (existRepost === null) {
			// Update repostee status
			Post.updateOne({ _id: repost._id }, {
				$inc: {
					repost_count: 1
				}
			});
		}
	}

	// Register to search database
	if (text != null) {
		es.index({
			index: 'misskey',
			type: 'post',
			id: post._id.toString(),
			body: {
				text: post.text
			}
		});

		const tokens = parse(text);

		// Extract a mentions
		const mentions = tokens
			.filter(t => t.type == 'mention')
			.map(m => m.username);

		mentions.forEach(async (mention) => {
			// Fetch mentioned user
			const mentionedUser = await User
				.findOne({ username_lower: mention.toLowerCase() });

			// Notify
			if (mentionedUser._id.toString() !== user._id.toString()) {
				notify(mentionedUser._id, 'mention', {
					post: post._id
				});
			}
		});

		// ハッシュタグ抽出
		//const hashtags = extractHashtags(text);

		// ハッシュタグをデータベースに登録
		//registerHashtags(user, hashtags);
	}
});
<|endoftext|>


<commit_before>var chai = require('chai')
  , expect = require('chai').expect
  , sinon = require('sinon')
  , Dispatcher = require('../../../lib/manager');

describe('integration: sso/oauth', function() {

  describe('redirect back from OAuth service provider', function() {

    // FIXME: make sure this is testing for correct behavior
    //        broke as a result fo removing _yield from Manager
    //        (same problem in oauth2 tests.)
    describe.skip('and returning to report with preserved state and session established by default handler', function() {
      var dispatcher = new Dispatcher()
        , request, response, err;

      before(function() {
        sinon.spy(dispatcher._store, 'load');
        sinon.spy(dispatcher._store, 'save');
        sinon.spy(dispatcher._store, 'update');
        sinon.spy(dispatcher._store, 'destroy');
      });

      before(function(done) {
        // TODO: test case with multiple handlers
        function handler(req, res, next) {
          req.federatedUser = { id: '248289761001', provider: 'http://server.example.com' };
          req.state.complete();
          next();
        }

        function defaultHandler(req, res, next) {
          req.session.user = { id: req.federatedUser.id };
          next();
        }

        chai.express.handler(dispatcher.flow(handler, [ defaultHandler ], { getHandle: function(req) { return 'oauth_' + req.query.oauth_token; } }))
          .req(function(req) {
            request = req;
            request.method = 'GET';
            request.url = '/oauth/callback?oauth_token=XXXXXXXX&oauth_verifier=YYYYYYYY';
            request.query = { oauth_token: 'XXXXXXXX', oauth_verifier: 'VVVVVVVV' };
            request.session = {};
            request.session.state = {};
            request.session.state['oauth_XXXXXXXX'] = {
              provider: 'http://server.example.com',
              returnTo: '/report/magic-quadrant',
              state: 'Dxh5N7w_wMQ',
              tokenSecret: 'XXXXXXXX-XXXXXXXX'
            };
            request.session.state['Dxh5N7w_wMQ'] = {
              accounts: [ { id: '1207059', provider: 'https://www.facebook.com' } ]
            };
          })
          .end(function(res) {
            response = res;
            done();
          })
          .next(function(err) {
            console.log(err)
          })
          .dispatch();
      });

      after(function() {
        dispatcher._store.destroy.restore();
        dispatcher._store.update.restore();
        dispatcher._store.save.restore();
        dispatcher._store.load.restore();
      });

      it('should correctly invoke state store', function() {
        expect(dispatcher._store.load).to.have.callCount(2);
        expect(dispatcher._store.save).to.have.callCount(0);
        expect(dispatcher._store.update).to.have.callCount(0);
        expect(dispatcher._store.destroy).to.have.callCount(1);
      });

      it('should set properties on request', function() {
        expect(request.federatedUser).to.be.an('object');
        expect(request.federatedUser).to.deep.equal({ id: '248289761001', provider: 'http://server.example.com' });
      });

      it('should update state', function() {
        expect(request.state).to.be.an('object');
        expect(request.state).to.deep.equal({
          accounts: [ { id: '1207059', provider: 'https://www.facebook.com' } ]
        });
      });

      it('should persist state in session', function() {
        expect(request.session).to.deep.equal({
          user: { id: '248289761001' },
          state: {
            'Dxh5N7w_wMQ': {
              accounts: [ { id: '1207059', provider: 'https://www.facebook.com' } ]
            }
          }
        });
      });

      it('should not set locals', function() {
        expect(request.locals).to.be.undefined;
      });

      it('should not set yieldState', function() {
        expect(request.yieldState).to.be.an('object'); // FIXME: remove yieldState
      });

      it('should redirect', function() {
        expect(response.statusCode).to.equal(302);
        expect(response.getHeader('Location')).to.equal('/report/magic-quadrant?state=Dxh5N7w_wMQ');
      });
    }); // and returning home with preserved state after default handling

  }); // redirect back from OAuth service provider

});<commit_msg>Comment out old dispatcher test from OAuth 1.0.<commit_after>var chai = require('chai')
  , expect = require('chai').expect
  , sinon = require('sinon');

describe('integration: sso/oauth', function() {

  describe('redirect back from OAuth service provider', function() {

    // FIXME: make sure this is testing for correct behavior
    //        broke as a result fo removing _yield from Manager
    //        (same problem in oauth2 tests.)

    /*
    describe.skip('and returning to report with preserved state and session established by default handler', function() {
      var dispatcher = new Dispatcher()
        , request, response, err;

      before(function() {
        sinon.spy(dispatcher._store, 'load');
        sinon.spy(dispatcher._store, 'save');
        sinon.spy(dispatcher._store, 'update');
        sinon.spy(dispatcher._store, 'destroy');
      });

      before(function(done) {
        // TODO: test case with multiple handlers
        function handler(req, res, next) {
          req.federatedUser = { id: '248289761001', provider: 'http://server.example.com' };
          req.state.complete();
          next();
        }

        function defaultHandler(req, res, next) {
          req.session.user = { id: req.federatedUser.id };
          next();
        }

        chai.express.handler(dispatcher.flow(handler, [ defaultHandler ], { getHandle: function(req) { return 'oauth_' + req.query.oauth_token; } }))
          .req(function(req) {
            request = req;
            request.method = 'GET';
            request.url = '/oauth/callback?oauth_token=XXXXXXXX&oauth_verifier=YYYYYYYY';
            request.query = { oauth_token: 'XXXXXXXX', oauth_verifier: 'VVVVVVVV' };
            request.session = {};
            request.session.state = {};
            request.session.state['oauth_XXXXXXXX'] = {
              provider: 'http://server.example.com',
              returnTo: '/report/magic-quadrant',
              state: 'Dxh5N7w_wMQ',
              tokenSecret: 'XXXXXXXX-XXXXXXXX'
            };
            request.session.state['Dxh5N7w_wMQ'] = {
              accounts: [ { id: '1207059', provider: 'https://www.facebook.com' } ]
            };
          })
          .end(function(res) {
            response = res;
            done();
          })
          .next(function(err) {
            console.log(err)
          })
          .dispatch();
      });

      after(function() {
        dispatcher._store.destroy.restore();
        dispatcher._store.update.restore();
        dispatcher._store.save.restore();
        dispatcher._store.load.restore();
      });

      it('should correctly invoke state store', function() {
        expect(dispatcher._store.load).to.have.callCount(2);
        expect(dispatcher._store.save).to.have.callCount(0);
        expect(dispatcher._store.update).to.have.callCount(0);
        expect(dispatcher._store.destroy).to.have.callCount(1);
      });

      it('should set properties on request', function() {
        expect(request.federatedUser).to.be.an('object');
        expect(request.federatedUser).to.deep.equal({ id: '248289761001', provider: 'http://server.example.com' });
      });

      it('should update state', function() {
        expect(request.state).to.be.an('object');
        expect(request.state).to.deep.equal({
          accounts: [ { id: '1207059', provider: 'https://www.facebook.com' } ]
        });
      });

      it('should persist state in session', function() {
        expect(request.session).to.deep.equal({
          user: { id: '248289761001' },
          state: {
            'Dxh5N7w_wMQ': {
              accounts: [ { id: '1207059', provider: 'https://www.facebook.com' } ]
            }
          }
        });
      });

      it('should not set locals', function() {
        expect(request.locals).to.be.undefined;
      });

      it('should not set yieldState', function() {
        expect(request.yieldState).to.be.an('object'); // FIXME: remove yieldState
      });

      it('should redirect', function() {
        expect(response.statusCode).to.equal(302);
        expect(response.getHeader('Location')).to.equal('/report/magic-quadrant?state=Dxh5N7w_wMQ');
      });
    }); // and returning home with preserved state after default handling
    */

  }); // redirect back from OAuth service provider

});<|endoftext|>


<commit_before>import React from "react";
import PropTypes from "prop-types";

import styled from "styled-components";
import style from "@agir/front/genericComponents/_variables.scss";

import StaticToast from "@agir/front/genericComponents/StaticToast";
import QuitEventButton from "./QuitEventButton";
import { RawFeatherIcon } from "@agir/front/genericComponents/FeatherIcon";

import { useSelector } from "@agir/front/globalContext/GlobalContext";
import { getUser } from "@agir/front/globalContext/reducers";
import Link from "@agir/front/app/Link";

const StyledContent = styled.div``;
const StyledJoin = styled.div`
  display: flex;
  margin-bottom: 0.5rem;

  &:last-child {
    margin-bottom: 0;
  }
`;

const GreenToast = styled(StaticToast)`
  border-radius: ${style.borderRadius};
  border-color: lightgrey;
  display: flex;
  flex-direction: column;

  && {
    margin-top: 0.5rem;
  }

  ${StyledContent} {
    flex: 1;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;

    justify-content: space-between;
    @media (max-width: ${style.collapse}px) {
      flex-direction: column;
      align-items: baseline;
    }
  }

  ${StyledContent}:last-child {
    margin-bottom: 0;
  }
`;

const Joined = ({ hasPrice, eventPk, group }) => (

  </StyledJoin>
);
Joined.propTypes = {
  hasPrice: PropTypes.bool,
  eventPk: PropTypes.string.isRequired,
  group: PropTypes.shape({
    id: PropTypes.string,
    name: PropTypes.string,
    isManager: PropTypes.bool,
  }),
};

const JoiningDetails = ({ id, hasPrice, rsvped, groups, logged }) => {
  const user = useSelector(getUser);
  const groupsId = groups?.map((group) => group.id) || [];

  // Get managing groups attendees
  const managingGroupsAttendees = user?.groups.filter(
    (group) => groupsId.includes(group.id) && group.isManager
  );

  if ((!rsvped || !logged) && !groups?.length) {
    return null;
  }
  return (
    <GreenToast $color="green">
      {logged && rsvped && <Joined eventPk={id} hasPrice={hasPrice} />}
      {managingGroupsAttendees.map((group) => (
        <Joined key={group.id} eventPk={id} hasPrice={hasPrice} group={group} />
      ))}
    </GreenToast>
  );
};
JoiningDetails.propTypes = {
  id: PropTypes.string.isRequired,
  hasPrice: PropTypes.bool,
  rsvped: PropTypes.bool,
  groups: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string,
      name: PropTypes.string,
      isManager: PropTypes.bool,
    })
  ),
  logged: PropTypes.bool,
};

export default JoiningDetails;
<commit_msg>fix: hide when no managing groups attendees<commit_after>import React from "react";
import PropTypes from "prop-types";

import styled from "styled-components";
import style from "@agir/front/genericComponents/_variables.scss";

import StaticToast from "@agir/front/genericComponents/StaticToast";
import QuitEventButton from "./QuitEventButton";
import { RawFeatherIcon } from "@agir/front/genericComponents/FeatherIcon";

import { useSelector } from "@agir/front/globalContext/GlobalContext";
import { getUser } from "@agir/front/globalContext/reducers";
import Link from "@agir/front/app/Link";

const StyledContent = styled.div``;
const StyledJoin = styled.div`
  display: flex;
  margin-bottom: 0.5rem;

  &:last-child {
    margin-bottom: 0;
  }
`;

const GreenToast = styled(StaticToast)`
  border-radius: ${style.borderRadius};
  border-color: lightgrey;
  display: flex;
  flex-direction: column;

  && {
    margin-top: 0.5rem;
  }

  ${StyledContent} {
    flex: 1;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;

    justify-content: space-between;
    @media (max-width: ${style.collapse}px) {
      flex-direction: column;
      align-items: baseline;
    }
  }

  ${StyledContent}:last-child {
    margin-bottom: 0;
  }
`;

const Joined = ({ hasPrice, eventPk, group }) => (

  </StyledJoin>
);
Joined.propTypes = {
  hasPrice: PropTypes.bool,
  eventPk: PropTypes.string.isRequired,
  group: PropTypes.shape({
    id: PropTypes.string,
    name: PropTypes.string,
    isManager: PropTypes.bool,
  }),
};

const JoiningDetails = ({ id, hasPrice, rsvped, groups, logged }) => {
  const user = useSelector(getUser);
  const groupsId = groups?.map((group) => group.id) || [];

  // Get managing groups attendees
  const managingGroupsAttendees = user?.groups.filter(
    (group) => groupsId.includes(group.id) && group.isManager
  );

  if ((!rsvped || !logged) && !managingGroupsAttendees?.length) {
    return null;
  }

  return (
    <GreenToast $color="green">
      {logged && rsvped && <Joined eventPk={id} hasPrice={hasPrice} />}
      {managingGroupsAttendees.map((group) => (
        <Joined key={group.id} eventPk={id} hasPrice={hasPrice} group={group} />
      ))}
    </GreenToast>
  );
};
JoiningDetails.propTypes = {
  id: PropTypes.string.isRequired,
  hasPrice: PropTypes.bool,
  rsvped: PropTypes.bool,
  groups: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string,
      name: PropTypes.string,
      isManager: PropTypes.bool,
    })
  ),
  logged: PropTypes.bool,
};

export default JoiningDetails;
<|endoftext|>


<commit_before>/**
 * @desc universalAnalytics() sends information to universalAnalytics
 *
 * @param {Number} experimentId - experimentId to send
 * @param {Number} customVariable - customVariable to send
 *
 * @return {undefined}
 */

function universalAnalytics(experimentId, customVariable) {

  $(function() {
    var EXPERIMENT_ID = experimentId;
    if (window.optimizely &&
      window.optimizely.variationMap.hasOwnProperty(EXPERIMENT_ID) &&
      window.ga && typeof ga === 'function') {
      var name = window.optimizely.data.experiments[EXPERIMENT_ID].name;
      var variation = window.optimizely.variationNamesMap[EXPERIMENT_ID];
      window.ga('set', 'dimension' + customVariable, name + ': ' + variation);
      window.ga('send', 'event', 'optimizely', name, variation, {
          'nonInteraction': 1
        });
    }
  });

}

export default universalAnalytics;
<commit_msg>fx param<commit_after>/**
 * @desc universalAnalytics() sends information to universalAnalytics
 *
 * @param {Number} experimentId - experimentId to send
 * @param {Number} customVariable - customVariable to send
 *
 * @return {undefined}
 */

function universalAnalytics(experimentId, customVariable) {

  $(function() {
    if (window.optimizely &&
      window.optimizely.variationMap.hasOwnProperty(experimentId) &&
      window.ga && typeof ga === 'function') {
      var name = window.optimizely.data.experiments[experimentId].name;
      var variation = window.optimizely.variationNamesMap[experimentId];
      window.ga('set', 'dimension' + customVariable, name + ': ' + variation);
      window.ga('send', 'event', 'optimizely', name, variation, {
          'nonInteraction': 1
        });
    }
  });

}

export default universalAnalytics;
<|endoftext|>


<commit_before>/*
slides new "pages" into view while sliding old out

depends on: tmlib base, jquery
params: arguments array: array of arguments
		elmsNavigation $element: container of navigation, supply only to auto-display on init, allowing hiding for non-js browsers

---css:
.container{
	position: relative; /*- non static
	width: 100px;
	height: 100px;
}
.page{
	display: none;
	position: absolute;
	top: 0;
	left: 0;
	width: 100px; /- same as above
	height: 100%;
}
.page.current{
	display: block;
}
.navigation{
	display: none;
}
body.hasjavascript .navigation{
	display: block;
}

---init
if(typeof $ !== 'undefined')
	$(document).ready(function(){
//		__.pageid = $("body").attr("id");
		var elmsBanners = $("#banner .banners .banner");
		if(elmsBanners.length > 0)
			__.bannerPager = new __.classes.pagerSlidingHash({elmsPages:elmsBanners, itemSelector:".banner", elmPreviousButton:$("#banner .navigation .previous"), elmNextButton:$("#banner .navigation .next"), elmsItemNavigation: $("#banner .navigation li"), regexHash: {find: /(#[\w-_])/, replace: "$1"}, callbackPreSlide: function(elmNewPage){
					var fncThis = this;
					var newTitle = elmNewPage.attr("data-title");
					this.boot.elmTitle.fadeOut(this.duration, function(){
						fncThis.boot.elmTitle.html(newTitle).fadeIn(fncThis.duration);
					});
				}
			});
	});

*/

/* --------
©pagerSlidingHash
------- */
__.classes.pagerSlidingHash = function(arguments){
		var fncThis = this;
		this.elmsPages = arguments.elmsPages || null;
		this.elmNavigation = arguments.elmNavigation || null;
		this.elmPreviousButton = arguments.elmPreviousButton || null;
		this.elmNextButton = arguments.elmNextButton || null;
		this.elmsItemNavigation = arguments.elmsItemNavigation || null;
		this.elmKeepHeight = arguments.elmKeepHeight || false;
			if(__.isIphone() == true) this.elmKeepHeight = false;
		this.itemSelector = (arguments.itemSelector !== undefined)? arguments.itemSelector: "item";
		this.classCurrentItem = (arguments.classCurrentItem !== undefined)? arguments.classCurrentItem: "selected";
		this.classPreviousItem = (arguments.classPreviousItem !== undefined)? arguments.classPreviousItem: "previous";
		this.classNextItem = (arguments.classNextItem !== undefined)? arguments.classNextItem: "next";
		this.classCurrentNavItem = arguments.classCurrentNavItem || "current";
		this.classDisabled = arguments.classDisabled || "disabled";
		this.classEnabled = arguments.classEnabled || "enabled";
		this.regexHash = arguments.regexHash || false;
		this.duration = (arguments.duration !== undefined)? arguments.duration: 500;
		this.contentWidth = (arguments.contentWidth !== undefined)? arguments.contentWidth: 960;
		this.contentLeft = arguments.contentLeft || 0;
		this.elmNavigationPointer = arguments.elmNavigationPointer || null;
		this.elmNavigationOrientation = arguments.elmNavigationOrientation || "horizontal";
		this.callbackPreSlide = arguments.callbackPreSlide || null;
		this.callbackPostSlide = arguments.callbackPostSlide || null;
		this.boot = arguments.boot || null;

		//--set up current pages
		if(window.location.hash){
			this.elmCurrent = this.elmsPages.filter(this.parsePath(window.location.hash)).addClass(this.classCurrentItem);
		}else{
			this.elmCurrent = this.elmsPages.filter("."+this.classCurrentItem);
			if(this.elmCurrent.length < 1){
				this.elmCurrent = this.elmsPages.first();
				this.elmCurrent.addClass(this.classCurrentItem);
			}
		}

		if(this.elmKeepHeight){
			this.elmKeepHeight.css("height", __.getHiddenElementHeight(this.elmCurrent));
		}

		//--show navigation
		if(this.elmNavigation)
			this.elmNavigation.show();

		//--set up page offsets
/* for whole body
		this.pageOffsetLeft = (arguments.contentLeft)? 0: this.elmCurrent.offset().left;
		if(!arguments.contentLeft){
			this.pageOffsetRight = (arguments.contentLeft)?false:$("body").outerWidth();

		}
*/
		this.pageOffsetLeft = arguments.pageOffsetLeft || 0;
		this.pageOffsetRight = arguments.pageOffsetRight || 0;

		//--set up non-current pages
		var elmsPrevious = this.elmsPages.filter("."+this.classCurrentItem).prevAll()
		elmsPrevious.addClass(this.classPreviousItem);
		var elmsNext = this.elmsPages.filter("."+this.classCurrentItem).nextAll()
		elmsNext.addClass(this.classNextItem);
		elmsPrevious.css({"left":0 - this.pageOffsetLeft - this.contentWidth, "display":"none"});
		elmsNext.css({"left":this.pageOffsetRight + this.contentWidth, "display":"none"});

		//--set up relative navigation
		this.updateRelativeNavigation(0);

		this.inprogress = 0;

		this.attachEvents();
	}
	__.classes.pagerSlidingHash.prototype.attachEvents = function(){
		var fncThis = this;
		if(fncThis.elmPreviousButton){
			fncThis.elmPreviousButton.children("a").bind("click", function(){
				if(fncThis.inprogress == 0 && $(this).attr("href")){
					fncThis.inprogress = 1;
					if(!fncThis.switchPagesByID(fncThis.parsePath(fncThis.elmPreviousButton.children("a").attr("href"))))
						fncThis.inprogress = 0;
				}
				return false;
			});
		}
		if(fncThis.elmNextButton){
			fncThis.elmNextButton.children("a").bind("click", function(){
				if(fncThis.inprogress == 0 && $(this).attr("href")){
					fncThis.inprogress = 1;
					if(!fncThis.switchPagesByID(fncThis.parsePath(fncThis.elmNextButton.children("a").attr("href"))))
						fncThis.inprogress = 0;
				}
				return false;
			});
		}
		if(fncThis.elmsItemNavigation){
			fncThis.elmsItemNavigation.bind("click", function(){
				if(fncThis.inprogress == 0){
					fncThis.inprogress = 1;
					if(!fncThis.switchPagesByID(fncThis.parsePath($(this).children("a").attr("href"))))
						fncThis.inprogress = 0;
				}
				return false;
			});
		}
	}
	__.classes.pagerSlidingHash.prototype.switchPagesByID = function(id){
		var elmNewPage = this.elmsPages.filter("#"+id);
		return this.switchPages(elmNewPage);
	}
	__.classes.pagerSlidingHash.prototype.switchPages = function(elmNewPage){
		if(elmNewPage[0] == this.elmCurrent[0]) return false;
		var fncThis = this;
		var callback = function(){
			fncThis.elmCurrent.removeClass(fncThis.classCurrentItem);
			elmNewPage.removeClass(fncThis.classNextItem).removeClass(fncThis.classPreviousItem).addClass(fncThis.classCurrentItem);
			fncThis.elmCurrent = elmNewPage;

			var elmsPagesLength = fncThis.elmsPages.length;
			var elmIndexCurrent = fncThis.getPageIndex(elmNewPage);
			//-set all previous elements to previous
			for(var i = 0; i < elmIndexCurrent; ++i){
				$(fncThis.elmsPages[i]).removeClass(fncThis.classNextItem).addClass(fncThis.classPreviousItem).css({"display":"none"});
			}
			//-set all next elements to next
			for(var i = elmIndexCurrent + 1; i < elmsPagesLength; ++i){
				$(fncThis.elmsPages[i]).addClass(fncThis.classNextItem).removeClass(fncThis.classPreviousItem).css({"display":"none"});
			}

			if(fncThis.callbackPostSlide)
				fncThis.callbackPostSlide.call(this);

			fncThis.updateRelativeNavigation();

			fncThis.inprogress = 0;
		}

		//--call preslide callback
		if(fncThis.callbackPreSlide)
			fncThis.callbackPreSlide.call(this, elmNewPage);

		//--animate new height
		if(fncThis.elmKeepHeight){
			fncThis.elmKeepHeight.animate({height: __.getHiddenElementHeight(elmNewPage)}, fncThis.duration);
		}

		//--swap pieces
		if(elmNewPage.hasClass(fncThis.classNextItem)){
			//--animate left
			fncThis.elmCurrent.css({"display":"block"}).animate({left: 0 - fncThis.pageOffsetLeft - fncThis.contentWidth}, fncThis.duration)
			elmNewPage.css({"display":"block", "left":this.pageOffsetRight + this.contentWidth}).animate({left: fncThis.contentLeft}, fncThis.duration+1, callback);
		}
		else{
			//--animate right
			fncThis.elmCurrent.css("display","block").animate({left: this.pageOffsetRight + this.contentWidth}, fncThis.duration)
			elmNewPage.css({"display":"block", "left":0 - this.pageOffsetLeft - this.contentWidth}).animate({left: fncThis.contentLeft}, fncThis.duration, callback);
		}

		return true;
	}
	__.classes.pagerSlidingHash.prototype.updateRelativeNavigation = function(argDuration){
		fncDuration = (typeof argDuration !== 'undefined')? argDuration: this.duration;

		if(this.elmPreviousButton){
			var previousURL = "#"+this.elmCurrent.prev(this.itemSelector).attr("id");
			if(previousURL != "#undefined")
				this.elmPreviousButton.removeClass(this.classDisabled).addClass(this.classEnabled).children("a").attr("href", previousURL);
			else
				this.elmPreviousButton.removeClass(this.classEnabled).addClass(this.classDisabled).children("a").attr("href", "");
		}
		if(this.elmNextButton){
		var nextURL = "#"+this.elmCurrent.next(this.itemSelector).attr("id");
			if(nextURL != "#undefined")
				this.elmNextButton.removeClass(this.classDisabled).addClass(this.classEnabled).children("a").attr("href", nextURL);
			else
				this.elmNextButton.removeClass(this.classEnabled).addClass(this.classDisabled).children("a").attr("href", "");
		}

		//-set up current navigation item
		if(this.elmsItemNavigation)
			this.elmsItemNavigation.removeClass(this.classCurrentNavItem).has("[href=#"+this.elmCurrent.attr("id")+"]").addClass(this.classCurrentNavItem);
		if(this.elmsItemNavigation && this.elmNavigationPointer){
			fncThis.pointToCurrentNavigation(this.elmsItemNavigation.filter("."+this.classCurrentNavItem));
			fncThis.elmNavigationPointer.css("display","block");
		}
	}
	__.classes.pagerSlidingHash.prototype.getPageIndex = function(argElement){
		var arrayLength = this.elmsPages.length;
		for(i=0;i<arrayLength;++i){
			if(this.elmsPages[i] == argElement[0]){
				return i;
			}
		}
	}
	__.classes.pagerSlidingHash.prototype.pointToCurrentNavigation = function(argID){
		if(this.elmsItemNavigation){
			var currentItem = this.elmsItemNavigation.filter("["+this.attrID+"="+argID+"]");
			this.elmsItemNavigation.filter(".selected").removeClass("selected");
			currentItem.addClass("selected");
			if(this.elmNavigationPointer){
				switch(this.elmNavigationOrientation){
					case "horizontal":
						this.elmNavigationPointer.animate({left: currentItem.position().left + currentItem.width()/2}, this.duration);
						break;
					case "vertical":
						this.elmNavigationPointer.animate({top: currentItem.position().top + currentItem.height()/2}, this.duration);
						break;
				}
			}
		}
	}
	__.classes.pagerSlidingHash.prototype.parsePath = function(path){
		if(this.regexHash)
			path = path.replace(this.regexHash.find, this.regexHash.replace);
		return this.escapeHash(path);
	}
	//-allows using slashes in the hash
	__.classes.pagerSlidingHash.prototype.escapeHash = function(hash){
		return hash.replace(/\//g, "\\/");
	}
	__.classes.pagerSlidingHash.prototype.unescapeHash = function(hash){
		return hash.replace(/\\\//g, "\/");
	}

	/*---notes:
	display changed for ie6 and 7 only so that scrollbar doesn't appear except on page change
	*/

<commit_msg>clientbehavior/pagers/pagerslidinghash: began margin tmlib dependencies 	removed some global scoped variables 	re?removed fixed class names 	default to current instead of selected for classname 	other minor cleanup<commit_after>/*
slides new "pages" into view while sliding old out

depends on: tmlib base: isIphone, getHiddenElementHeight
	jquery
params: arguments array: array of arguments
		elmsNavigation $element: container of navigation, supply only to auto-display on init, allowing hiding for non-js browsers

---css:
.container{
	overflow: hidden;
	position: relative; /*- non static
	width: 100px;
	height: 100px;
}
.page{
	position: absolute;
	top: 0;
	left: 0;
	width: 100px; /- same as above
	height: 100%;
}
.navigation{
	display: none;
}
body.hasjavascript .navigation{
	display: block;
}

---init
if(typeof $ !== 'undefined')
	$(document).ready(function(){
//		__.pageid = $("body").attr("id");
		var elmsBanners = $("#banner .banners .banner");
		if(elmsBanners.length > 0)
			__.bannerPager = new __.classes.pagerSlidingHash({elmsPages:elmsBanners, itemSelector:".banner", elmPreviousButton:$("#banner .navigation .previous"), elmNextButton:$("#banner .navigation .next"), elmsItemNavigation: $("#banner .navigation li"), regexHash: {find: /(#[\w-_])/, replace: "$1"}, callbackPreSlide: function(elmNewPage){
					var fncThis = this;
					var newTitle = elmNewPage.attr("data-title");
					this.boot.elmTitle.fadeOut(this.duration, function(){
						fncThis.boot.elmTitle.html(newTitle).fadeIn(fncThis.duration);
					});
				}
			});
	});

*/

/* --------
©pagerSlidingHash
------- */
__.classes.pagerSlidingHash = function(arguments){
		var fncThis = this;
		this.elmsPages = arguments.elmsPages || null;
		this.elmNavigation = arguments.elmNavigation || null;
		this.elmPreviousButton = arguments.elmPreviousButton || null;
		this.elmNextButton = arguments.elmNextButton || null;
		this.elmsItemNavigation = arguments.elmsItemNavigation || null;
		this.elmKeepHeight = arguments.elmKeepHeight || false;
			if(__.isIphone() == true) this.elmKeepHeight = false;
		this.itemSelector = (arguments.itemSelector !== undefined)? arguments.itemSelector: "item";
		this.classCurrentItem = (arguments.classCurrentItem !== undefined)? arguments.classCurrentItem: "current";
		this.classPreviousItem = (arguments.classPreviousItem !== undefined)? arguments.classPreviousItem: "previous";
		this.classNextItem = (arguments.classNextItem !== undefined)? arguments.classNextItem: "next";
		this.classCurrentNavItem = arguments.classCurrentNavItem || "current";
		this.classDisabled = arguments.classDisabled || "disabled";
		this.classEnabled = arguments.classEnabled || "enabled";
		this.regexHash = arguments.regexHash || false;
		this.duration = (arguments.duration !== undefined)? arguments.duration: 500;
		this.contentWidth = (arguments.contentWidth !== undefined)? arguments.contentWidth: 960;
		this.contentLeft = arguments.contentLeft || 0;
		this.elmNavigationPointer = arguments.elmNavigationPointer || null;
		this.elmNavigationOrientation = arguments.elmNavigationOrientation || "horizontal";
		this.callbackPreSlide = arguments.callbackPreSlide || null;
		this.callbackPostSlide = arguments.callbackPostSlide || null;
		this.boot = arguments.boot || null;

		//--set up current pages
		if(window.location.hash){
			this.elmCurrent = this.elmsPages.filter(this.parsePath(window.location.hash)).addClass(this.classCurrentItem);
		}else{
			this.elmCurrent = this.elmsPages.filter("."+this.classCurrentItem);
			if(this.elmCurrent.length < 1){
				this.elmCurrent = this.elmsPages.first();
				this.elmCurrent.addClass(this.classCurrentItem);
			}
		}

		if(this.elmKeepHeight){
			this.elmKeepHeight.css("height", __.getHiddenElementHeight(this.elmCurrent));
		}

		//--show navigation
		if(this.elmNavigation)
			this.elmNavigation.show();

		//--set up page offsets
/* for whole body
		this.pageOffsetLeft = (arguments.contentLeft)? 0: this.elmCurrent.offset().left;
		if(!arguments.contentLeft){
			this.pageOffsetRight = (arguments.contentLeft)?false:$("body").outerWidth();

		}
*/
		this.pageOffsetLeft = arguments.pageOffsetLeft || 0;
		this.pageOffsetRight = arguments.pageOffsetRight || 0;

		//--set up non-current pages
		var elmsPrevious = this.elmsPages.filter("."+this.classCurrentItem).prevAll()
		elmsPrevious.addClass(this.classPreviousItem);
		var elmsNext = this.elmsPages.filter("."+this.classCurrentItem).nextAll()
		elmsNext.addClass(this.classNextItem);
		elmsPrevious.css({"left":0 - this.pageOffsetLeft - this.contentWidth, "display":"none"});
		elmsNext.css({"left":this.pageOffsetRight + this.contentWidth, "display":"none"});

		//--set up relative navigation
		this.updateRelativeNavigation(0);

		this.inprogress = 0;

		this.attachEvents();
	}
	__.classes.pagerSlidingHash.prototype.attachEvents = function(){
		var fncThis = this;
		if(fncThis.elmPreviousButton){
			fncThis.elmPreviousButton.children("a").bind("click", function(){
				if(fncThis.inprogress == 0 && $(this).attr("href")){
					fncThis.inprogress = 1;
					if(!fncThis.switchPagesByID(fncThis.parsePath(fncThis.elmPreviousButton.children("a").attr("href"))))
						fncThis.inprogress = 0;
				}
				return false;
			});
		}
		if(fncThis.elmNextButton){
			fncThis.elmNextButton.children("a").bind("click", function(){
				if(fncThis.inprogress == 0 && $(this).attr("href")){
					fncThis.inprogress = 1;
					if(!fncThis.switchPagesByID(fncThis.parsePath(fncThis.elmNextButton.children("a").attr("href"))))
						fncThis.inprogress = 0;
				}
				return false;
			});
		}
		if(fncThis.elmsItemNavigation){
			fncThis.elmsItemNavigation.bind("click", function(){
				if(fncThis.inprogress == 0){
					fncThis.inprogress = 1;
					if(!fncThis.switchPagesByID(fncThis.parsePath($(this).children("a").attr("href"))))
						fncThis.inprogress = 0;
				}
				return false;
			});
		}
	}
	__.classes.pagerSlidingHash.prototype.switchPagesByID = function(id){
		var elmNewPage = this.elmsPages.filter("#"+id);
		return this.switchPages(elmNewPage);
	}
	__.classes.pagerSlidingHash.prototype.switchPages = function(elmNewPage){
		if(elmNewPage[0] == this.elmCurrent[0]) return false;
		var fncThis = this;
		var callback = function(){
			fncThis.elmCurrent.removeClass(fncThis.classCurrentItem);
			elmNewPage.removeClass(fncThis.classNextItem).removeClass(fncThis.classPreviousItem).addClass(fncThis.classCurrentItem);
			fncThis.elmCurrent = elmNewPage;

			var elmsPagesLength = fncThis.elmsPages.length;
			var elmIndexCurrent = fncThis.getPageIndex(elmNewPage);
			//-set all previous elements to previous
			for(var i = 0; i < elmIndexCurrent; ++i){
				$(fncThis.elmsPages[i]).removeClass(fncThis.classNextItem).addClass(fncThis.classPreviousItem).css({"display":"none"});
			}
			//-set all next elements to next
			for(var i = elmIndexCurrent + 1; i < elmsPagesLength; ++i){
				$(fncThis.elmsPages[i]).addClass(fncThis.classNextItem).removeClass(fncThis.classPreviousItem).css({"display":"none"});
			}

			if(fncThis.callbackPostSlide)
				fncThis.callbackPostSlide.call(this);

			fncThis.updateRelativeNavigation();

			fncThis.inprogress = 0;
		}

		//--call preslide callback
		if(fncThis.callbackPreSlide)
			fncThis.callbackPreSlide.call(this, elmNewPage);

		//--animate new height
		if(fncThis.elmKeepHeight){
			fncThis.elmKeepHeight.animate({height: __.getHiddenElementHeight(elmNewPage)}, fncThis.duration);
		}

		//--swap pieces
		if(elmNewPage.hasClass(fncThis.classNextItem)){
			//--animate left
			fncThis.elmCurrent.css({"display":"block"}).animate({left: 0 - fncThis.pageOffsetLeft - fncThis.contentWidth}, fncThis.duration)
			elmNewPage.css({"display":"block", "left":this.pageOffsetRight + this.contentWidth}).animate({left: fncThis.contentLeft}, fncThis.duration+1, callback);
		}
		else{
			//--animate right
			fncThis.elmCurrent.css("display","block").animate({left: this.pageOffsetRight + this.contentWidth}, fncThis.duration)
			elmNewPage.css({"display":"block", "left":0 - this.pageOffsetLeft - this.contentWidth}).animate({left: fncThis.contentLeft}, fncThis.duration, callback);
		}

		return true;
	}
	__.classes.pagerSlidingHash.prototype.updateRelativeNavigation = function(argDuration){
		var fncDuration = (typeof argDuration !== 'undefined')? argDuration: this.duration;

		if(this.elmPreviousButton){
			var previousURL = "#"+this.elmCurrent.prev(this.itemSelector).attr("id");
			if(previousURL != "#undefined")
				this.elmPreviousButton.removeClass(this.classDisabled).addClass(this.classEnabled).children("a").attr("href", previousURL);
			else
				this.elmPreviousButton.removeClass(this.classEnabled).addClass(this.classDisabled).children("a").attr("href", "");
		}
		if(this.elmNextButton){
		var nextURL = "#"+this.elmCurrent.next(this.itemSelector).attr("id");
			if(nextURL != "#undefined")
				this.elmNextButton.removeClass(this.classDisabled).addClass(this.classEnabled).children("a").attr("href", nextURL);
			else
				this.elmNextButton.removeClass(this.classEnabled).addClass(this.classDisabled).children("a").attr("href", "");
		}

		//-set up current navigation item
		if(this.elmsItemNavigation)
			this.elmsItemNavigation.removeClass(this.classCurrentNavItem).has("[href=#"+this.elmCurrent.attr("id")+"]").addClass(this.classCurrentNavItem);
		if(this.elmsItemNavigation && this.elmNavigationPointer){
			fncThis.pointToCurrentNavigation(this.elmsItemNavigation.filter("."+this.classCurrentNavItem));
			fncThis.elmNavigationPointer.css("display","block");
		}
	}
	__.classes.pagerSlidingHash.prototype.getPageIndex = function(argElement){
		var arrayLength = this.elmsPages.length;
		for(var i=0;i<arrayLength;++i){
			if(this.elmsPages[i] == argElement[0]){
				return i;
			}
		}
	}
	__.classes.pagerSlidingHash.prototype.pointToCurrentNavigation = function(argID){
		if(this.elmsItemNavigation){
			var currentItem = this.elmsItemNavigation.filter("["+this.attrID+"="+argID+"]");
			this.elmsItemNavigation.filter("."+this.classCurrentNavItem).removeClass(this.classCurrentNavItem);
			currentItem.addClass(this.classCurrentNavItem);
			if(this.elmNavigationPointer){
				switch(this.elmNavigationOrientation){
					case "horizontal":
						this.elmNavigationPointer.animate({left: currentItem.position().left + currentItem.width()/2}, this.duration);
						break;
					case "vertical":
						this.elmNavigationPointer.animate({top: currentItem.position().top + currentItem.height()/2}, this.duration);
						break;
				}
			}
		}
	}
	__.classes.pagerSlidingHash.prototype.parsePath = function(path){
		if(this.regexHash)
			path = path.replace(this.regexHash.find, this.regexHash.replace);
		return this.escapeHash(path);
	}
	//-allows using slashes in the hash
	__.classes.pagerSlidingHash.prototype.escapeHash = function(hash){
		return hash.replace(/\//g, "\\/");
	}
	__.classes.pagerSlidingHash.prototype.unescapeHash = function(hash){
		return hash.replace(/\\\//g, "\/");
	}

	/*---notes:
	display changed for ie6 and 7 only so that scrollbar doesn't appear except on page change
	*/

<|endoftext|>


<commit_before>(function($) {
    'use strict';

    var sortAsNumber = function(a, b) {

        var aAsNumber = parseFloat(a);
        var bAsNumber = parseFloat(b);
        return aAsNumber - bAsNumber;

    };

    var sortAsString = function(a, b) {

        if(a === b) return 0;
        return a > b ? 1 : -1;

    };

    var sortAsDate = function(a, b) {

        var aAsDate = new Date(a);
        var bAsDate = new Date(b);
        if(aAsDate === bAsDate) return 0;
        return aAsDate > bAsDate ? 1 : -1;

    };

    var sort = function(column, type, direction) {

        var $column = $(column);
        var tBody = $column.closest('table').find('tbody');
        var tableRows = tBody.children();
        var columnIndex = $column.parent().children().index($column);

        if(typeof type === 'function') {
            tableRows.sort(function(a, b) {
                var valA = $($(a).children().get(columnIndex)).text();
                var valB = $($(b).children().get(columnIndex)).text();
                return type(valA, valB);
            });
        } else {
            tableRows.sort(function(a, b) {
                var valA = $($(a).children().get(columnIndex)).text();
                var valB = $($(b).children().get(columnIndex)).text();
                switch(type) {
                    case 'Number':
                        return sortAsNumber(valA, valB);
                    case 'String':
                        return sortAsString(valA, valB);
                    case 'Date':
                        return sortAsDate(valA, valB);
                    default:
                        throw new Error('Unsupported type ' + type);
                }
            });
        }

        tableRows.remove();
        if(direction === 'descending') {
            tableRows = tableRows.toArray().reverse();
        }
        tBody.append(tableRows);

    };

    var init = function(opts) {

        var column;
        var currentDirections = {};

        for(column in opts.columns) {
            if(opts.columns.hasOwnProperty(column)) {
                currentDirections[column] = undefined;
            }
        }

        this.find('thead>tr>th')
            .filter(function() {
                return opts.columns.hasOwnProperty($(this).text());
            })
            .css(opts.css)
            .click(function(e) {
                var columnHeader = $(this).text();
                if(currentDirections[columnHeader] === undefined ||
                        currentDirections[columnHeader] === 'descending') {
                    currentDirections[columnHeader] = 'ascending';
                } else if(currentDirections[columnHeader] === 'ascending') {
                    currentDirections[columnHeader] = 'descending';
                }
                sort(this, opts.columns[columnHeader], currentDirections[columnHeader]);
            });

    };

    $.fn.sorTable = function(method, direction, type) {

        //initialization
        if(direction === undefined && type === undefined){
            var opts = $.extend({}, $.fn.sorTable.defaults, method);
            init.apply(this, [opts]);
        } else {
            var column = this.find('thead>tr>th:contains("' + method + '")');
            if(column.length === 0){
                throw new Error('No column with name ' + method);
            }
            sort(column, type, direction);
        }

    };

    $.fn.sorTable.defaults = {

        css: {
            cursor: "pointer"
        }

    };

})(jQuery);
<commit_msg>preserve event listeners on table cell elements<commit_after>(function($) {
    'use strict';

    var sortAsNumber = function(a, b) {

        var aAsNumber = parseFloat(a);
        var bAsNumber = parseFloat(b);
        return aAsNumber - bAsNumber;

    };

    var sortAsString = function(a, b) {

        if(a === b) return 0;
        return a > b ? 1 : -1;

    };

    var sortAsDate = function(a, b) {

        var aAsDate = new Date(a);
        var bAsDate = new Date(b);
        if(aAsDate === bAsDate) return 0;
        return aAsDate > bAsDate ? 1 : -1;

    };

    var sort = function(column, type, direction) {

        var $column = $(column);
        var tBody = $column.closest('table').find('tbody');
        var tableRows = tBody.children();
        var columnIndex = $column.parent().children().index($column);

        if(typeof type === 'function') {
            tableRows.sort(function(a, b) {
                var valA = $($(a).children().get(columnIndex)).text();
                var valB = $($(b).children().get(columnIndex)).text();
                return type(valA, valB);
            });
        } else {
            tableRows.sort(function(a, b) {
                var valA = $($(a).children().get(columnIndex)).text();
                var valB = $($(b).children().get(columnIndex)).text();
                switch(type) {
                    case 'Number':
                        return sortAsNumber(valA, valB);
                    case 'String':
                        return sortAsString(valA, valB);
                    case 'Date':
                        return sortAsDate(valA, valB);
                    default:
                        throw new Error('Unsupported type ' + type);
                }
            });
        }

        //preserve event listeners which were bound to table cells
        var sortedRows = tableRows.clone(true);
        tableRows.remove();
        if(direction === 'descending') {
            sortedRows = tableRows.toArray().reverse();
        }
        tBody.append(sortedRows);

    };

    var init = function(opts) {

        var column;
        var currentDirections = {};

        for(column in opts.columns) {
            if(opts.columns.hasOwnProperty(column)) {
                currentDirections[column] = undefined;
            }
        }

        this.find('thead>tr>th')
            .filter(function() {
                return opts.columns.hasOwnProperty($(this).text());
            })
            .css(opts.css)
            .click(function(e) {
                var columnHeader = $(this).text();
                if(currentDirections[columnHeader] === undefined ||
                        currentDirections[columnHeader] === 'descending') {
                    currentDirections[columnHeader] = 'ascending';
                } else if(currentDirections[columnHeader] === 'ascending') {
                    currentDirections[columnHeader] = 'descending';
                }
                sort(this, opts.columns[columnHeader], currentDirections[columnHeader]);
            });

    };

    $.fn.sorTable = function(method, direction, type) {

        //initialization
        if(direction === undefined && type === undefined){
            var opts = $.extend({}, $.fn.sorTable.defaults, method);
            init.apply(this, [opts]);
        } else {
            var column = this.find('thead>tr>th:contains("' + method + '")');
            if(column.length === 0){
                throw new Error('No column with name ' + method);
            }
            sort(column, type, direction);
        }

    };

    $.fn.sorTable.defaults = {

        css: {
            cursor: "pointer"
        }

    };

})(jQuery);
<|endoftext|>


<commit_before>// Get the url to our folder where NeatPlayer.JS is being stored
// Not really reliable, but works for the current usage.
var scriptEls = document.getElementsByTagName( 'script' );
var thisScriptEl = scriptEls[scriptEls.length - 1];
var scriptPath = thisScriptEl.src;
var scriptFolder = scriptPath.substr(0, scriptPath.lastIndexOf( '/' ) + 1);

var baseUrl = scriptFolder;

$(function($){
  $.fn.NeatPlayer = function(opts) {
    // Setting default options if none given
    opts = $.extend({
      width: 640,
      height: 360
    }, opts);

    var parent = this, // Our parent-element
        media = [], // Holds all our media-information
        currentSong = 0;

    /**
     * Get all the media-elements and set up variables
     *
     */
    var init = function() {
      var audioElements = parent.find('audio');

      $.each(audioElements, function(i, el) {
        el = $(el);
        media.push(el[0]);

        // Set the type
        media[media.length - 1].type = el[0].src.split('.').pop().toLowerCase();

        // Set a title if none has been specified
        if (media[media.length - 1].title === '') {
          media[media.length - 1].title = el[0].src.split('/').pop().replace(/%20/g, " ");
        }

        // Transform the duration-value into a readable string
        media[media.length - 1].clearDuration = '';
        var duration = media[media.length - 1].duration;

        // Hours
        if (duration >= 3600) {
          // Kolla noll
        }

        // Minutes
        if (duration >= 60) {
          // Kolla noll
        }

        // Seconds
        if (duration > 0) {
          // Kolla noll
        }
      });

      console.log(media);
    };

    /**
     * Draws all the graphic elements in the parent-element.
     *
     */
    var draw = function() {
      var playerArea = $('
');
      playerArea
        .width(opts.width)
        .height(opts.height);

      parent.append(playerArea);

      // Setting up the first element to be played
      playerArea.append(
        $('
')
          .html(media[0].title + '
' + media[0].duration)
      );

      // Adding controls
      var controls =
        $('
')
          .addClass('NeatPlayerControls')
          .addClass('default')
          .width(playerArea.width())
          .height(playerArea.height() * 0.1);

      var padding = 0.95;
      controls
        .append(
          $('
')
            .addClass('NeatPlayer-previous')
            .append(
              $('')
                .attr('src', baseUrl + 'img/next-icon-white.png')
                .attr('height', (controls.height() * padding))
                .click(previous)
            )
        )
        .append(
          $('
')
            .addClass('NeatPlayer-playpause')
            .append(
              $('')
                .attr('src', baseUrl + 'img/pause-icon-white.png')
                .attr('height', (controls.height() * padding))
                .click(pause)
            )
            .append(
              $('')
                .attr('src', baseUrl + 'img/play-icon-white.png')
                .attr('height', (controls.height() * padding))
                .click(play)
            )
        )
        .append(
          $('
')
            .addClass('NeatPlayer-next')
            .append(
              $('')
                .attr('src', baseUrl + 'img/next-icon-white.png')
                .attr('height', (controls.height() * padding))
                .click(next)
            )
        );

      parent.append(controls);
    };

    /**
     * Plays the previous song
     *
     */
    var previous = function() {
      console.log('Starting the previous song...')
    };

    /**
     * Pauses the current song
     *
     */
    var pause = function() {
      console.log('Pausing song...')
    };

    /**
     * Plays the current song
     *
     */
    var play = function() {
      console.log('Playing song...')
    };

    /**
     * Plays the next song
     *
     */
    var next = function() {
      console.log('Playing next song...')
    };

    // Initiate plugin
    init();
    // Drawing the player
    draw();

  };
});
<commit_msg>Fixed the calculation for the duration in cleartext<commit_after>// Get the url to our folder where NeatPlayer.JS is being stored
// Not really reliable, but works for the current usage.
var scriptEls = document.getElementsByTagName( 'script' );
var thisScriptEl = scriptEls[scriptEls.length - 1];
var scriptPath = thisScriptEl.src;
var scriptFolder = scriptPath.substr(0, scriptPath.lastIndexOf( '/' ) + 1);

var baseUrl = scriptFolder;

$(function($){
  $.fn.NeatPlayer = function(opts) {
    // Setting default options if none given
    opts = $.extend({
      width: 640,
      height: 360
    }, opts);

    var parent = this, // Our parent-element
        media = [], // Holds all our media-information
        currentSong = 0;

    /**
     * Get all the media-elements and set up variables
     *
     */
    var init = function() {
      var audioElements = parent.find('audio');

      $.each(audioElements, function(i, el) {
        el = $(el);
        media.push(el[0]);

        // Set the type
        media[media.length - 1].type = el[0].src.split('.').pop().toLowerCase();

        // Set a title if none has been specified
        if (media[media.length - 1].title === '') {
          media[media.length - 1].title = el[0].src.split('/').pop().replace(/%20/g, " ");
        }

        // Transform the duration-value into a readable string
        media[media.length - 1].clearDuration = '';
        var duration = media[media.length - 1].duration,
            hours, // If the file doesn't have a duration longer than 1 h we never want to display 00:00:00
            minutes = '00',
            seconds = '00';

        // Hours
        if (Math.floor(duration) >= 3600) {
          if (Math.floor(duration / 3600) < 10) {
            hours = '0' + Math.floor(duration / 3600);
          } else {
            hours = Math.floor(duration / 3600);
          }

          duration -= (Math.floor(duration / 3600) * 3600);
        }

        // Minutes
        if (Math.floor(duration) >= 60) {
          if (Math.floor(duration / 60) < 10) {
            minutes = '0' + Math.floor(duration / 60);
          } else {
            minutes = Math.floor(duration / 60);
          }

          duration -= (Math.floor(duration / 60) * 60);
        }

        // Seconds
        if (Math.floor(duration) > 0) {
          if (Math.floor(duration) < 10) {
            seconds = '0' + Math.floor(duration);
          } else {
            seconds = Math.floor(duration);
          }
        }

        media[media.length - 1].clearDuration = (hours ? hours + ':' : '') + minutes + ':' + seconds;
      });

      console.log(media);
    };

    /**
     * Draws all the graphic elements in the parent-element.
     *
     */
    var draw = function() {
      var playerArea = $('
');
      playerArea
        .width(opts.width)
        .height(opts.height);

      parent.append(playerArea);

      // Setting up the first element to be played
      playerArea.append(
        $('
')
          .html(media[0].title + '
' + media[0].clearDuration)
      );

      // Adding controls
      var controls =
        $('
')
          .addClass('NeatPlayerControls')
          .addClass('default')
          .width(playerArea.width())
          .height(playerArea.height() * 0.1);

      var padding = 0.95;
      controls
        .append(
          $('
')
            .addClass('NeatPlayer-previous')
            .append(
              $('')
                .attr('src', baseUrl + 'img/next-icon-white.png')
                .attr('height', (controls.height() * padding))
                .click(previous)
            )
        )
        .append(
          $('
')
            .addClass('NeatPlayer-playpause')
            .append(
              $('')
                .attr('src', baseUrl + 'img/pause-icon-white.png')
                .attr('height', (controls.height() * padding))
                .click(pause)
            )
            .append(
              $('')
                .attr('src', baseUrl + 'img/play-icon-white.png')
                .attr('height', (controls.height() * padding))
                .click(play)
            )
        )
        .append(
          $('
')
            .addClass('NeatPlayer-next')
            .append(
              $('')
                .attr('src', baseUrl + 'img/next-icon-white.png')
                .attr('height', (controls.height() * padding))
                .click(next)
            )
        );

      parent.append(controls);
    };

    /**
     * Plays the previous song
     *
     */
    var previous = function() {
      console.log('Starting the previous song...')
    };

    /**
     * Pauses the current song
     *
     */
    var pause = function() {
      console.log('Pausing song...')
    };

    /**
     * Plays the current song
     *
     */
    var play = function() {
      console.log('Playing song...')
    };

    /**
     * Plays the next song
     *
     */
    var next = function() {
      console.log('Playing next song...')
    };

    // Initiate plugin
    init();
    // Drawing the player
    draw();

  };
});
<|endoftext|>


<commit_before>import Notifications from './collection.js';
import Users from 'meteor/nova:users';
import { addCallback, newMutation } from 'meteor/nova:core';

const createNotifications = (userIds, notificationType, documentType, documentId) => {
  userIds.forEach(userId => {

    let user = Users.findOne(userId);

    let notificationData = {
      userId: userId,
      documentId: documentId,
      type: documentType,
      notificationMessage: notificationType
    }

    newMutation({
      action: 'notifications.new',
      collection: Notifications,
      document: notificationData,
      currentUser: user,
      validate: false
    });
  });
}

// hard dependency on Comments and Posts packages
const Comments = Package['nova:comments'].default;
const Posts = Package['nova:posts'].default;

/**
 * @summary Add notification callback when a post is approved
 */
const PostsApprovedNotification = (post) => {
  createNotifications([post.userId], 'postApproved', 'post', post._id);
}
addCallback("posts.approve.async", PostsApprovedNotification);

/**
 * @summary Add new post notification callback on post submit
 */
const PostsNewNotifications = (post) => {

  let adminIds = _.pluck(Users.adminUsers({fields: {_id:1}}), '_id');
  let usersToNotify = _.pluck(Users.find({'notifications_posts': true}, {fields: {_id:1}}).fetch(), '_id');

  // remove post author ID from arrays
  adminIds = _.without(adminIds, post.userId);
  usersToNotify = _.without(usersToNotify, post.userId);

  if (post.status === Posts.config.STATUS_PENDING) {
    // if post is pending, only notify admins
    createNotifications(adminIds, 'newPendingPost', 'post', post._id);
  } else {
    // if post is approved, notify everybody
    createNotifications(usersToNotify, 'newPost', 'post', post._id);
  }
}
addCallback("posts.new.async", PostsNewNotifications);

// add new comment notification callback on comment submit
const CommentsNewNotifications = (comment) => {

  // note: dummy content has disableNotifications set to true
  if(Meteor.isServer && !comment.disableNotifications) {

    const post = Posts.findOne(comment.postId);

    // keep track of whom we've notified (so that we don't notify the same user twice for one comment,
    // if e.g. they're both the author of the post and the author of a comment being replied to)
    let notifiedUsers = [];

    // 1. Notify author of comment being replied to
    if (!!comment.parentCommentId) {
      const parentComment = Comments.findOne(comment.parentCommentId);

      // do not notify author of parent comment if they're replying to their own comment
      if (parentComment.userId !== comment.userId) {
        const parentCommentAuthor = Users.findOne(parentComment.userId);

        // do not notify parent comment author if they have reply notifications turned off
        if (Users.getSetting(parentCommentAuthor, "notifications_replies", true)) {
          createNotifications([parentCommentAuthor.userId], 'newReply', 'comment', comment._id);
          notifiedUsers.push(parentCommentAuthor.userId);
        }
      }
    }

    // 2. Notify author of post (if they have new comment notifications turned on)
    //    but do not notify author of post if they're the ones posting the comment
    //    nor if we just sent them a newReply notification above
    const postAuthor = Users.findOne(post.userId);
    if (comment.userId !== postAuthor._id && !_.contains(notifiedUsers, postAuthor._id)
        && Users.getSetting(postAuthor, "notifications_comments", true)) {

      createNotifications([postAuthor._id], 'newComment', 'comment', comment._id);
      notifiedUsers.push(postAuthor._id);
    }

    // ROGTODO: allow author to unsubscribe, and only notify subscribers
    // 3. Notify users who are subscribed to the post
    console.log('post.subscribers:');
    console.log(post.subscribers);
    if (!!post.subscribers && !!post.subscribers.length) {
      // remove userIds of users that have already been notified
      // and of comment author (they could be replying in a thread they're subscribed to)
      let subscribersToNotify = _.difference(post.subscribers, notifiedUsers, [comment.userId]);
      createNotifications(subscribersToNotify, 'newCommentSubscribed', 'comment', comment._id);
    }
  }
}
addCallback("comments.new.async", CommentsNewNotifications);
<commit_msg>remove debugging code<commit_after>import Notifications from './collection.js';
import Users from 'meteor/nova:users';
import { addCallback, newMutation } from 'meteor/nova:core';

const createNotifications = (userIds, notificationType, documentType, documentId) => {
  userIds.forEach(userId => {

    let user = Users.findOne(userId);

    let notificationData = {
      userId: userId,
      documentId: documentId,
      type: documentType,
      notificationMessage: notificationType
    }

    newMutation({
      action: 'notifications.new',
      collection: Notifications,
      document: notificationData,
      currentUser: user,
      validate: false
    });
  });
}

// hard dependency on Comments and Posts packages
const Comments = Package['nova:comments'].default;
const Posts = Package['nova:posts'].default;

/**
 * @summary Add notification callback when a post is approved
 */
const PostsApprovedNotification = (post) => {
  createNotifications([post.userId], 'postApproved', 'post', post._id);
}
addCallback("posts.approve.async", PostsApprovedNotification);

/**
 * @summary Add new post notification callback on post submit
 */
const PostsNewNotifications = (post) => {

  let adminIds = _.pluck(Users.adminUsers({fields: {_id:1}}), '_id');
  let usersToNotify = _.pluck(Users.find({'notifications_posts': true}, {fields: {_id:1}}).fetch(), '_id');

  // remove post author ID from arrays
  adminIds = _.without(adminIds, post.userId);
  usersToNotify = _.without(usersToNotify, post.userId);

  if (post.status === Posts.config.STATUS_PENDING) {
    // if post is pending, only notify admins
    createNotifications(adminIds, 'newPendingPost', 'post', post._id);
  } else {
    // if post is approved, notify everybody
    createNotifications(usersToNotify, 'newPost', 'post', post._id);
  }
}
addCallback("posts.new.async", PostsNewNotifications);

// add new comment notification callback on comment submit
const CommentsNewNotifications = (comment) => {

  // note: dummy content has disableNotifications set to true
  if(Meteor.isServer && !comment.disableNotifications) {

    const post = Posts.findOne(comment.postId);

    // keep track of whom we've notified (so that we don't notify the same user twice for one comment,
    // if e.g. they're both the author of the post and the author of a comment being replied to)
    let notifiedUsers = [];

    // 1. Notify author of comment being replied to
    if (!!comment.parentCommentId) {
      const parentComment = Comments.findOne(comment.parentCommentId);

      // do not notify author of parent comment if they're replying to their own comment
      if (parentComment.userId !== comment.userId) {
        const parentCommentAuthor = Users.findOne(parentComment.userId);

        // do not notify parent comment author if they have reply notifications turned off
        if (Users.getSetting(parentCommentAuthor, "notifications_replies", true)) {
          createNotifications([parentCommentAuthor.userId], 'newReply', 'comment', comment._id);
          notifiedUsers.push(parentCommentAuthor.userId);
        }
      }
    }

    // 2. Notify author of post (if they have new comment notifications turned on)
    //    but do not notify author of post if they're the ones posting the comment
    //    nor if we just sent them a newReply notification above
    const postAuthor = Users.findOne(post.userId);
    if (comment.userId !== postAuthor._id && !_.contains(notifiedUsers, postAuthor._id)
        && Users.getSetting(postAuthor, "notifications_comments", true)) {

      createNotifications([postAuthor._id], 'newComment', 'comment', comment._id);
      notifiedUsers.push(postAuthor._id);
    }

    // ROGTODO: allow author to unsubscribe, and only notify subscribers
    // 3. Notify users who are subscribed to the post
    if (!!post.subscribers && !!post.subscribers.length) {
      // remove userIds of users that have already been notified
      // and of comment author (they could be replying in a thread they're subscribed to)
      let subscribersToNotify = _.difference(post.subscribers, notifiedUsers, [comment.userId]);
      createNotifications(subscribersToNotify, 'newCommentSubscribed', 'comment', comment._id);
    }
  }
}
addCallback("comments.new.async", CommentsNewNotifications);
<|endoftext|>


<commit_before>'use strict';

var log         = require('../log')(module);
var User        = require('../models').User;
var Message     = require('../models').Message;

module.exports = {
    getInbox : function (req, res, next) {
        // GET /api/messages/inbox
        var currentUser = req.user;
        Message.find({ 'to': currentUser._id })
            .populate('from to', 'username firstName lastName imageUrl')
            .exec(function (err, messages) {
                if (err) {
                    res.send(err);
                    return log('Messages could not be loaded: ' + err);
                }

                res.send(messages);
                // Mark as read after sent
                messages.forEach(function (m) {
                    m.isRead = true;
                    m.save();
                });
            });
    },
    getSent : function (req, res, next) {
        // GET /api/messages/inbox
        var currentUser = req.user;
        Message.find({ 'from': currentUser._id })
            .populate('from to', 'username firstName lastName imageUrl')
            .exec(function (err, messages) {
                if (err) {
                    res.send(err);
                    return log('Messages could not be loaded: ' + err);
                }
                // DO NOT MARK AS READ !!!
                res.send(messages);
            });
    },
    getAllWithUser : function (req, res, next) {
        // GET /api/messages/withUser/:username
        var currentUser = req.user;
        User.findOne({ username: req.params.username }).exec(function (err, otherUser) {
            if (otherUser) {

                if (otherUser._id.equals(currentUser._id)) {
                    return res.status(404).send({message: 'You cannot have messages from yourself!'});
                }

                Message.find({
                        $or:
                        [
                         { $and:[ {'from': currentUser._id}, {'to': otherUser._id} ] },
                         { $and:[ {'from': otherUser._id}, {'to': currentUser._id} ] }
                        ]
                    })
                    .populate('from to', 'username firstName lastName imageUrl')
                    .exec(function (err, messages) {
                        if (err) {
                            res.send(err);
                            return log('Messages could not be loaded: ' + err);
                        }

                        res.send({messages:messages});
                        // Mark as read after sent
                        messages.forEach(function (m) {
                            if (!m.isRead && m.to._id.equals(currentUser._id)) {
                                m.isRead = true;
                                m.save();
                            }
                        });
                    });

            } else {
                res.status(404).send('Other user not found!');
            }
        });
    },
    getMessageById: function (req, res, next) {
        // GET /api/messages/:id
        var currentUser = req.user;
        Message.findOne(
            {
                $and:
                [
                 {_id: req.params.id},
                 { $or:[ {'from': currentUser._id}, {'to': currentUser._id} ] }
                ]
            })
            .populate('from to', 'username firstName lastName imageUrl')
            .exec(function (err, message) {
                if (err) {
                    res.send(err);
                    return log('Message could not be loaded: ' + err);
                }

                if (message) {
                    res.send(message);
                    // Mark as read
                    if (!message.isRead && message.to.username == currentUser.username) {
                        message.isRead = true;
                        message.save();
                    }
                } else {
                    res.status(404).send('Message not found!');
                }
            });
    },
    sendMessage: function (req, res, next) {
        // POST api/messages/send/:username
        var sender = req.user;
        User.findOne({ username: req.params.username }).exec(function (err, receiver) {
            if (receiver) {

                if (receiver._id.equals(sender._id)) {
                    return res.status(404).send({message: 'You cannot send message to your self!'});
                }

                var message = new Message({
                    title: req.body.title,
                    content: req.body.content,
                    date: new Date(),
                    from: sender,
                    to: receiver,
                    isRead: false
                });
                message.save(function (err) {
                    if (err) {
                        res.status(400).send(err);
                        return log('Error in saving message' + err);
                    }

                    // Send the user view models
                    var messageViewModel = {
                        title: message.title,
                        content: message.content,
                        date: message.date,
                        isRead: message.isRead
                    };
                    messageViewModel.from = {
                        userId: sender.userId,
                        username: sender.username,
                        firstName: sender.firstName,
                        lastName: sender.lastName,
                        imageUrl: sender.imageUrl
                    };
                    messageViewModel.to = {
                        userId: receiver.userId,
                        username: receiver.username,
                        firstName: receiver.firstName,
                        lastName: receiver.lastName,
                        imageUrl: receiver.imageUrl
                    };
                    res.send({message:messageViewModel});

                    // Send push notification for new message to the receiver
                });

                receiver.messages.push(message);
                receiver.save();
            } else {
                res.status(404).send('User not found!');
            }
        });
    }
}<commit_msg>adde message view model<commit_after>'use strict';

var log         = require('../log')(module);
var User        = require('../models').User;
var Message     = require('../models').Message;

module.exports = {
    getInbox : function (req, res, next) {
        // GET /api/messages/inbox
        var currentUser = req.user;
        Message.find({ 'to': currentUser._id })
            .populate('from to', 'username firstName lastName imageUrl')
            .exec(function (err, messages) {
                if (err) {
                    res.send(err);
                    return log('Messages could not be loaded: ' + err);
                }

                res.send(messages);
                // Mark as read after sent
                messages.forEach(function (m) {
                    m.isRead = true;
                    m.save();
                });
            });
    },
    getSent : function (req, res, next) {
        // GET /api/messages/inbox
        var currentUser = req.user;
        Message.find({ 'from': currentUser._id })
            .populate('from to', 'username firstName lastName imageUrl')
            .exec(function (err, messages) {
                if (err) {
                    res.send(err);
                    return log('Messages could not be loaded: ' + err);
                }
                // DO NOT MARK AS READ !!!
                res.send(messages);
            });
    },
    getAllWithUser : function (req, res, next) {
        // GET /api/messages/withUser/:username?unread=true
        var currentUser = req.user;
        var unread = req.query.unread || false;

        var query = User.findOne({ username: req.params.username });

        if (unread) {
            query.where({isRead: false});
        }

//        User.findOne({ username: req.params.username })
//            //.where({ isRead: req.query.status !== 'unread'})
            query.exec(function (err, otherUser) {
                if (otherUser) {

                    if (otherUser._id.equals(currentUser._id)) {
                        return res.status(404).send({message: 'You cannot have messages from yourself!'});
                    }

                    Message.find({
                            $or:
                            [
                             { $and:[ {'from': currentUser._id}, {'to': otherUser._id} ] },
                             { $and:[ {'from': otherUser._id}, {'to': currentUser._id} ] }
                            ]
                        })
                        .populate('from to', 'username firstName lastName imageUrl')
                        .exec(function (err, messages) {
                            if (err) {
                                res.send(err);
                                return log('Messages could not be loaded: ' + err);
                            }

                            res.send({messages:messages});
                            // Mark as read after sent
                            messages.forEach(function (m) {
                                if (!m.isRead && m.to._id.equals(currentUser._id)) {
                                    m.isRead = true;
                                    m.save();
                                }
                            });
                        });

                } else {
                    res.status(404).send('Other user not found!');
                }
            });
    },
    getMessageById: function (req, res, next) {
        // GET /api/messages/:id
        var currentUser = req.user;
        Message.findOne(
            {
                $and:
                [
                 {_id: req.params.id},
                 { $or:[ {'from': currentUser._id}, {'to': currentUser._id} ] }
                ]
            })
            .populate('from to', 'username firstName lastName imageUrl')
            .exec(function (err, message) {
                if (err) {
                    res.send(err);
                    return log('Message could not be loaded: ' + err);
                }

                if (message) {
                    res.send(message);
                    // Mark as read
                    if (!message.isRead && message.to.username == currentUser.username) {
                        message.isRead = true;
                        message.save();
                    }
                } else {
                    res.status(404).send('Message not found!');
                }
            });
    },
    sendMessage: function (req, res, next) {
        // POST api/messages/send/:username
        var sender = req.user;
        User.findOne({ username: req.params.username }).exec(function (err, receiver) {
            if (receiver) {

                if (receiver._id.equals(sender._id)) {
                    return res.status(404).send({message: 'You cannot send message to your self!'});
                }

                var message = new Message({
                    title: req.body.title,
                    content: req.body.content,
                    date: new Date(),
                    from: sender,
                    to: receiver,
                    isRead: false
                });
                message.save(function (err) {
                    if (err) {
                        res.status(400).send(err);
                        return log('Error in saving message' + err);
                    }

                    // Send the user view models
                    var messageViewModel = {
                        title: message.title,
                        content: message.content,
                        date: message.date,
                        isRead: message.isRead
                    };
                    messageViewModel.from = {
                        userId: sender.userId,
                        username: sender.username,
                        firstName: sender.firstName,
                        lastName: sender.lastName,
                        imageUrl: sender.imageUrl
                    };
                    messageViewModel.to = {
                        userId: receiver.userId,
                        username: receiver.username,
                        firstName: receiver.firstName,
                        lastName: receiver.lastName,
                        imageUrl: receiver.imageUrl
                    };
                    res.send({message:messageViewModel});

                    // Send push notification for new message to the receiver
                });

                receiver.messages.push(message);
                receiver.save();
            } else {
                res.status(404).send('User not found!');
            }
        });
    }
}<|endoftext|>


<commit_before>// Copyright 2019-2022, University of Colorado Boulder

/**
 * Checkbox for showing/hiding grid lines for a graph.
 * See https://github.com/phetsims/graphing-lines/issues/91.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */

import { Shape } from '../../kite/js/imports.js';
import merge from '../../phet-core/js/merge.js';
import { Path } from '../../scenery/js/imports.js';
import Checkbox from '../../sun/js/Checkbox.js';
import sceneryPhet from './sceneryPhet.js';

class GridCheckbox extends Checkbox {

  /**
   * @param {Property.<boolean>} property
   * @param {Object} [options]
   */
  constructor( property, options ) {

    options = merge( {

      // options for the grid icon
      gridSize: 30, // {number} square grid with this width and height
      gridStroke: 'rgb( 100, 100, 100 )',
      gridLineWidth: 1,

      // superclass options
      spacing: 10

    }, options );

    const iconSize = options.gridSize;

    // The grid shape looks approximately like this:
    //
    //      |   |   |
    //   ---|---|---|---
    //      |   |   |
    //   ---|---|---|---
    //      |   |   |
    //   ---|---|---|---
    //      |   |   |
    //
    const iconShape = new Shape()
      .moveTo( iconSize / 4, 0 )
      .lineTo( iconSize / 4, iconSize )
      .moveTo( iconSize / 2, 0 )
      .lineTo( iconSize / 2, iconSize )
      .moveTo( iconSize * 3 / 4, 0 )
      .lineTo( iconSize * 3 / 4, iconSize )
      .moveTo( 0, iconSize / 4 )
      .lineTo( iconSize, iconSize / 4 )
      .moveTo( 0, iconSize / 2 )
      .lineTo( iconSize, iconSize / 2 )
      .moveTo( 0, iconSize * 3 / 4 )
      .lineTo( iconSize, iconSize * 3 / 4 );

    const iconNode = new Path( iconShape, {
      stroke: options.gridStroke,
      lineWidth: options.lineWidth
    } );

    super( iconNode, property, options );
  }
}

sceneryPhet.register( 'GridCheckbox', GridCheckbox );
export default GridCheckbox;<commit_msg>add @ts-nocheck before renaming to .ts<commit_after>// Copyright 2019-2022, University of Colorado Boulder

// @ts-nocheck
/**
 * Checkbox for showing/hiding grid lines for a graph.
 * See https://github.com/phetsims/graphing-lines/issues/91.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */

import { Shape } from '../../kite/js/imports.js';
import merge from '../../phet-core/js/merge.js';
import { Path } from '../../scenery/js/imports.js';
import Checkbox from '../../sun/js/Checkbox.js';
import sceneryPhet from './sceneryPhet.js';

class GridCheckbox extends Checkbox {

  /**
   * @param {Property.<boolean>} property
   * @param {Object} [options]
   */
  constructor( property, options ) {

    options = merge( {

      // options for the grid icon
      gridSize: 30, // {number} square grid with this width and height
      gridStroke: 'rgb( 100, 100, 100 )',
      gridLineWidth: 1,

      // superclass options
      spacing: 10

    }, options );

    const iconSize = options.gridSize;

    // The grid shape looks approximately like this:
    //
    //      |   |   |
    //   ---|---|---|---
    //      |   |   |
    //   ---|---|---|---
    //      |   |   |
    //   ---|---|---|---
    //      |   |   |
    //
    const iconShape = new Shape()
      .moveTo( iconSize / 4, 0 )
      .lineTo( iconSize / 4, iconSize )
      .moveTo( iconSize / 2, 0 )
      .lineTo( iconSize / 2, iconSize )
      .moveTo( iconSize * 3 / 4, 0 )
      .lineTo( iconSize * 3 / 4, iconSize )
      .moveTo( 0, iconSize / 4 )
      .lineTo( iconSize, iconSize / 4 )
      .moveTo( 0, iconSize / 2 )
      .lineTo( iconSize, iconSize / 2 )
      .moveTo( 0, iconSize * 3 / 4 )
      .lineTo( iconSize, iconSize * 3 / 4 );

    const iconNode = new Path( iconShape, {
      stroke: options.gridStroke,
      lineWidth: options.lineWidth
    } );

    super( iconNode, property, options );
  }
}

sceneryPhet.register( 'GridCheckbox', GridCheckbox );
export default GridCheckbox;<|endoftext|>