<commit_before>import fetch from 'isomorphic-fetch';
import { getToken } from './auth';
import { api as apiConfig, frontendPreferences as frontendPrefsConfig } from '../config';

const getRequestOptions = () => ({
  headers: {
    'Accept': 'application/json',
    'X-Authentication-Token': getToken()
  }
});

export function getWhoAmI() {
  return fetch(`${apiConfig.host}/v2/users/whoami`, getRequestOptions());
}

export function getHome({ offset }) {
  return fetch(
    `${apiConfig.host}/v1/timelines/home?offset=${offset}`, getRequestOptions());
}

export function getDiscussions({ offset }) {
  return fetch(
    `${apiConfig.host}/v1/timelines/filter/discussions?offset=${offset}`, getRequestOptions());
}

export function getDirect({ offset }) {
  return fetch(
    `${apiConfig.host}/v1/timelines/filter/directs?offset=${offset}`, getRequestOptions());
}

export function getUserFeed({ username, offset }) {
  return fetch(
    `${apiConfig.host}/v1/timelines/${username}?offset=${offset}`, getRequestOptions());
}

const MAX_COMMENTS = 2;

export function getLikesOnly({ postId, commentsExpanded }) {
  const maxComments = commentsExpanded ? 'all' : `${MAX_COMMENTS}`;
  return fetch(
    `${apiConfig.host}/v1/posts/${postId}?maxComments=${maxComments}&maxLikes=all`, getRequestOptions());
}

export function getPost({ postId, maxComments = MAX_COMMENTS }) {
  return fetch(
    `${apiConfig.host}/v1/posts/${postId}?maxComments=${maxComments}`, getRequestOptions());
}

export function createPost({ feeds, postText, attachmentIds, more }) {
  return fetch(`${apiConfig.host}/v1/posts`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({
      post: {
        body: postText,
        attachments: attachmentIds
      },
      meta: {
        feeds: feeds,
        commentsDisabled: !!more.commentsDisabled
      }
    })
  });
}

export function createBookmarkletPost({ feeds, postText, imageUrls, commentText }) {
  return fetch(`${apiConfig.host}/v1/bookmarklet`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({
      title: postText,
      images: imageUrls,
      comment: commentText,
      meta: {
        feeds: feeds
      }
    })
  });
}

export function updatePost({ postId, newPost }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ post: newPost })
  });
}

export function deletePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}`, {
    'method': 'DELETE',
    'headers': {
      'Accept': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function addComment({ postId, commentText }) {
  return fetch(`${apiConfig.host}/v1/comments`, {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ comment: { body: commentText, postId } })
  });
}

export function updateComment({ commentId, newCommentBody }) {
  return fetch(`${apiConfig.host}/v1/comments/${commentId}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ comment: { body: newCommentBody } })
  });
}

export function deleteComment({ commentId }) {
  return fetch(`${apiConfig.host}/v1/comments/${commentId}`, {
    'method': 'DELETE',
    'headers': {
      'Accept': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function likePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/like`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function unlikePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/unlike`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function hidePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/hide`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function unhidePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/unhide`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function disableComments({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/disableComments`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function enableComments({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/enableComments`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function signIn({ username, password }) {
  const encodedBody = _.map({ username, password },
    (value, key) => key + '=' + encodeURIComponent(value)
  ).join('&');

  return fetch(`${apiConfig.host}/v1/session`, {
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    method: 'POST',
    body: encodedBody
  });
}

export function signUp({ username, password, email, captcha }) {
  const encodedBody = _.map({ username, password, email, captcha },
    (value, key) => key + '=' + encodeURIComponent(value)
  ).join('&');

  return fetch(`${apiConfig.host}/v1/users`, {
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    method: 'POST',
    body: encodedBody
  });
}

export function updateUser({ id, screenName, email, description, isPrivate, isProtected }) {
  return fetch(`${apiConfig.host}/v1/users/${id}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ user: { screenName, email, description, isPrivate, isProtected } })
  });
}

export function updateFrontendPreferences({ userId, prefs }) {
  const frontendPreferences = {
    [frontendPrefsConfig.clientId]: prefs
  };

  return fetch(`${apiConfig.host}/v1/users/${userId}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ user: { frontendPreferences } })
  });
}

export function updatePassword({ currentPassword, password, passwordConfirmation }) {
  const encodedBody = _.map({ currentPassword, password, passwordConfirmation },
    (value, key) => key + '=' + encodeURIComponent(value)
  ).join('&');

  return fetch(`${apiConfig.host}/v1/users/updatePassword`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
      'X-Authentication-Token': getToken()
    },
    'body': encodedBody
  });
}

export function updateUserPicture({ picture }) {
  let data = new FormData();
  data.append('file', picture);

  return fetch(`${apiConfig.host}/v1/users/updateProfilePicture`, {
    'method': 'POST',
    'headers': {
      'X-Authentication-Token': getToken()
    },
    'body': data
  });
}

const userAction = action => ({ username }) => {
  return fetch(`${apiConfig.host}/v1/users/${username}/${action}`, {
    method: 'POST',
    'headers': {
      'X-Authentication-Token': getToken(),
    },
  });
};

export const ban = userAction('ban');
export const unban = userAction('unban');
export const subscribe = userAction('subscribe');
export const unsubscribe = userAction('unsubscribe');
export const sendSubscriptionRequest = userAction('sendRequest');

export function getUserComments({ username, offset }) {
  return fetch(`${apiConfig.host}/v1/timelines/${username}/comments?offset=${offset}`, getRequestOptions());
}

export function getUserLikes({ username, offset }) {
  return fetch(`${apiConfig.host}/v1/timelines/${username}/likes?offset=${offset}`, getRequestOptions());
}

export function getUserSubscribers({ username }) {
  return fetch(`${apiConfig.host}/v1/users/${username}/subscribers`, getRequestOptions());
}

export function getUserSubscriptions({ username }) {
  return fetch(`${apiConfig.host}/v1/users/${username}/subscriptions`, getRequestOptions());
}

export function getUserInfo({ username }) {
  return fetch(`${apiConfig.host}/v1/users/${username}`, getRequestOptions());
}

export function createGroup(groupSettings) {
  return fetch(`${apiConfig.host}/v1/groups`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ group: groupSettings })
  });
}

export function updateGroup({ id, groupSettings }) {
  return fetch(`${apiConfig.host}/v1/users/${id}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ user: groupSettings })
  });
}

export function updateGroupPicture({ groupName, file }) {
  let data = new FormData();
  data.append('file', file);

  return fetch(`${apiConfig.host}/v1/groups/${groupName}/updateProfilePicture`, {
    'method': 'POST',
    'headers': {
      'X-Authentication-Token': getToken()
    },
    'body': data
  });
}

export function acceptGroupRequest({ groupName, userName }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/acceptRequest/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function rejectGroupRequest({ groupName, userName }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/rejectRequest/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function acceptUserRequest({ userName }) {
  return fetch(`${apiConfig.host}/v1/users/acceptRequest/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function rejectUserRequest({ userName }) {
  return fetch(`${apiConfig.host}/v1/users/rejectRequest/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function unsubscribeFromGroup({ groupName, userName }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/unsubscribeFromGroup/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function promoteGroupAdmin({ groupName, user }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/subscribers/${user.username}/admin`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function demoteGroupAdmin({ groupName, user }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/subscribers/${user.username}/unadmin`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function revokeSentRequest({ username }) {
  return fetch(`${apiConfig.host}/v2/requests/${username}/revoke`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function getBlockedByMe() {
  return fetch(`${apiConfig.host}/v2/users/blockedByMe`, getRequestOptions());
}

export function getSearchResults({ query, offset }) {
  // For empty query, just mock the call to API without actual request/response
  if (!query) {
    return new Promise(function(resolve) {
      resolve({
        status: 200,
        json: () => new Promise(function(resolve) { resolve({}); })
      });
    });
  }

  const encodedQuery = encodeURIComponent(query);
  return fetch(`${apiConfig.host}/v2/search?qs=${encodedQuery}&offset=${offset}`, getRequestOptions());
}

export function getUnreadDirects() {
  return fetch(`${apiConfig.host}/v2/users/getUnreadDirectsNumber`, getRequestOptions());
}

export function markDirectsAsRead() {
  return fetch(`${apiConfig.host}/v2/users/markAllDirectsAsRead`, getRequestOptions());
}
<commit_msg>Switch homefeed to /v2/timelines/home<commit_after>import fetch from 'isomorphic-fetch';
import { getToken } from './auth';
import { api as apiConfig, frontendPreferences as frontendPrefsConfig } from '../config';

const getRequestOptions = () => ({
  headers: {
    'Accept': 'application/json',
    'X-Authentication-Token': getToken()
  }
});

export function getWhoAmI() {
  return fetch(`${apiConfig.host}/v2/users/whoami`, getRequestOptions());
}

export function getHome({ offset }) {
  return fetch(
    `${apiConfig.host}/v2/timelines/home?offset=${offset}`, getRequestOptions());
}

export function getDiscussions({ offset }) {
  return fetch(
    `${apiConfig.host}/v1/timelines/filter/discussions?offset=${offset}`, getRequestOptions());
}

export function getDirect({ offset }) {
  return fetch(
    `${apiConfig.host}/v1/timelines/filter/directs?offset=${offset}`, getRequestOptions());
}

export function getUserFeed({ username, offset }) {
  return fetch(
    `${apiConfig.host}/v1/timelines/${username}?offset=${offset}`, getRequestOptions());
}

const MAX_COMMENTS = 2;

export function getLikesOnly({ postId, commentsExpanded }) {
  const maxComments = commentsExpanded ? 'all' : `${MAX_COMMENTS}`;
  return fetch(
    `${apiConfig.host}/v1/posts/${postId}?maxComments=${maxComments}&maxLikes=all`, getRequestOptions());
}

export function getPost({ postId, maxComments = MAX_COMMENTS }) {
  return fetch(
    `${apiConfig.host}/v1/posts/${postId}?maxComments=${maxComments}`, getRequestOptions());
}

export function createPost({ feeds, postText, attachmentIds, more }) {
  return fetch(`${apiConfig.host}/v1/posts`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({
      post: {
        body: postText,
        attachments: attachmentIds
      },
      meta: {
        feeds: feeds,
        commentsDisabled: !!more.commentsDisabled
      }
    })
  });
}

export function createBookmarkletPost({ feeds, postText, imageUrls, commentText }) {
  return fetch(`${apiConfig.host}/v1/bookmarklet`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({
      title: postText,
      images: imageUrls,
      comment: commentText,
      meta: {
        feeds: feeds
      }
    })
  });
}

export function updatePost({ postId, newPost }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ post: newPost })
  });
}

export function deletePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}`, {
    'method': 'DELETE',
    'headers': {
      'Accept': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function addComment({ postId, commentText }) {
  return fetch(`${apiConfig.host}/v1/comments`, {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ comment: { body: commentText, postId } })
  });
}

export function updateComment({ commentId, newCommentBody }) {
  return fetch(`${apiConfig.host}/v1/comments/${commentId}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ comment: { body: newCommentBody } })
  });
}

export function deleteComment({ commentId }) {
  return fetch(`${apiConfig.host}/v1/comments/${commentId}`, {
    'method': 'DELETE',
    'headers': {
      'Accept': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function likePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/like`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function unlikePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/unlike`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function hidePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/hide`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function unhidePost({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/unhide`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function disableComments({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/disableComments`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function enableComments({ postId }) {
  return fetch(`${apiConfig.host}/v1/posts/${postId}/enableComments`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': '{}'
  });
}

export function signIn({ username, password }) {
  const encodedBody = _.map({ username, password },
    (value, key) => key + '=' + encodeURIComponent(value)
  ).join('&');

  return fetch(`${apiConfig.host}/v1/session`, {
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    method: 'POST',
    body: encodedBody
  });
}

export function signUp({ username, password, email, captcha }) {
  const encodedBody = _.map({ username, password, email, captcha },
    (value, key) => key + '=' + encodeURIComponent(value)
  ).join('&');

  return fetch(`${apiConfig.host}/v1/users`, {
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    method: 'POST',
    body: encodedBody
  });
}

export function updateUser({ id, screenName, email, description, isPrivate, isProtected }) {
  return fetch(`${apiConfig.host}/v1/users/${id}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ user: { screenName, email, description, isPrivate, isProtected } })
  });
}

export function updateFrontendPreferences({ userId, prefs }) {
  const frontendPreferences = {
    [frontendPrefsConfig.clientId]: prefs
  };

  return fetch(`${apiConfig.host}/v1/users/${userId}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ user: { frontendPreferences } })
  });
}

export function updatePassword({ currentPassword, password, passwordConfirmation }) {
  const encodedBody = _.map({ currentPassword, password, passwordConfirmation },
    (value, key) => key + '=' + encodeURIComponent(value)
  ).join('&');

  return fetch(`${apiConfig.host}/v1/users/updatePassword`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
      'X-Authentication-Token': getToken()
    },
    'body': encodedBody
  });
}

export function updateUserPicture({ picture }) {
  let data = new FormData();
  data.append('file', picture);

  return fetch(`${apiConfig.host}/v1/users/updateProfilePicture`, {
    'method': 'POST',
    'headers': {
      'X-Authentication-Token': getToken()
    },
    'body': data
  });
}

const userAction = action => ({ username }) => {
  return fetch(`${apiConfig.host}/v1/users/${username}/${action}`, {
    method: 'POST',
    'headers': {
      'X-Authentication-Token': getToken(),
    },
  });
};

export const ban = userAction('ban');
export const unban = userAction('unban');
export const subscribe = userAction('subscribe');
export const unsubscribe = userAction('unsubscribe');
export const sendSubscriptionRequest = userAction('sendRequest');

export function getUserComments({ username, offset }) {
  return fetch(`${apiConfig.host}/v1/timelines/${username}/comments?offset=${offset}`, getRequestOptions());
}

export function getUserLikes({ username, offset }) {
  return fetch(`${apiConfig.host}/v1/timelines/${username}/likes?offset=${offset}`, getRequestOptions());
}

export function getUserSubscribers({ username }) {
  return fetch(`${apiConfig.host}/v1/users/${username}/subscribers`, getRequestOptions());
}

export function getUserSubscriptions({ username }) {
  return fetch(`${apiConfig.host}/v1/users/${username}/subscriptions`, getRequestOptions());
}

export function getUserInfo({ username }) {
  return fetch(`${apiConfig.host}/v1/users/${username}`, getRequestOptions());
}

export function createGroup(groupSettings) {
  return fetch(`${apiConfig.host}/v1/groups`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ group: groupSettings })
  });
}

export function updateGroup({ id, groupSettings }) {
  return fetch(`${apiConfig.host}/v1/users/${id}`, {
    'method': 'PUT',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    },
    'body': JSON.stringify({ user: groupSettings })
  });
}

export function updateGroupPicture({ groupName, file }) {
  let data = new FormData();
  data.append('file', file);

  return fetch(`${apiConfig.host}/v1/groups/${groupName}/updateProfilePicture`, {
    'method': 'POST',
    'headers': {
      'X-Authentication-Token': getToken()
    },
    'body': data
  });
}

export function acceptGroupRequest({ groupName, userName }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/acceptRequest/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function rejectGroupRequest({ groupName, userName }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/rejectRequest/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function acceptUserRequest({ userName }) {
  return fetch(`${apiConfig.host}/v1/users/acceptRequest/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function rejectUserRequest({ userName }) {
  return fetch(`${apiConfig.host}/v1/users/rejectRequest/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function unsubscribeFromGroup({ groupName, userName }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/unsubscribeFromGroup/${userName}`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function promoteGroupAdmin({ groupName, user }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/subscribers/${user.username}/admin`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function demoteGroupAdmin({ groupName, user }) {
  return fetch(`${apiConfig.host}/v1/groups/${groupName}/subscribers/${user.username}/unadmin`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function revokeSentRequest({ username }) {
  return fetch(`${apiConfig.host}/v2/requests/${username}/revoke`, {
    'method': 'POST',
    'headers': {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Authentication-Token': getToken()
    }
  });
}

export function getBlockedByMe() {
  return fetch(`${apiConfig.host}/v2/users/blockedByMe`, getRequestOptions());
}

export function getSearchResults({ query, offset }) {
  // For empty query, just mock the call to API without actual request/response
  if (!query) {
    return new Promise(function(resolve) {
      resolve({
        status: 200,
        json: () => new Promise(function(resolve) { resolve({}); })
      });
    });
  }

  const encodedQuery = encodeURIComponent(query);
  return fetch(`${apiConfig.host}/v2/search?qs=${encodedQuery}&offset=${offset}`, getRequestOptions());
}

export function getUnreadDirects() {
  return fetch(`${apiConfig.host}/v2/users/getUnreadDirectsNumber`, getRequestOptions());
}

export function markDirectsAsRead() {
  return fetch(`${apiConfig.host}/v2/users/markAllDirectsAsRead`, getRequestOptions());
}
<|endoftext|>


<commit_before>var INDEX = require('../index.js')

const fakenews = {
  title: 'is fake',
  body: 'is also fake'
}

var mongo = INDEX.mongo

const CNN = new mongo.models.FakeNews(fakenews)

CNN.save()
  .then(res => {
    console.log(res)
  })
  .catch(e => console.log(e))
<commit_msg>test fix<commit_after>var INDEX = require('../index.js')

const fakenews = {
  title: 'is fake',
  body: 'is also fake'
}

var mongo = INDEX.mongo

const CNN = new mongo.models.fakenews(fakenews)

CNN.save()
  .then(res => {
    console.log(res)
  })
  .catch(e => console.log(e))
<|endoftext|>


<commit_before>function checkCard(card) {
        switch (card) {
            case "J" :
                card = 11;
                break;
            case "Q" :
                card = 12;
                break;
            case "K" :
                card = 13;
                break;
            case "A" :
                card = 14;
                break;
            default:
                card = card;
        }
    }

module.exports = {
    checkCard: checkCard,
}
<commit_msg>Min bet calculator<commit_after>function minbet(gs) {
  return gs.current_buy_in - gs.players[in_action][bet] + gs.minimum_raise;
}

function checkCard(card) {
        switch (card) {
            case "J" :
                card = 11;
                break;
            case "Q" :
                card = 12;
                break;
            case "K" :
                card = 13;
                break;
            case "A" :
                card = 14;
                break;
            default:
                card = card;
        }
    }

module.exports = {
    checkCard: checkCard,
}
<|endoftext|>


<commit_before>/*!
 * Annotations Support for Patterns - v0.3
 *
 * Copyright (c) 2013 Dave Olsen, http://dmolsen.com
 * Licensed under the MIT license
 *
 */

var annotationsPattern = {

	commentsOverlayActive:  false,
	commentsOverlay:        false,
	commentsOverlayElement: "",
	commentsEmbeddedActive: false,
	commentsEmbedded:       false,
	commentsGathered:       { "commentOverlay": "on", "count": 0, "comments": { } },

	/**
	* record which annotations are related to this pattern so they can be sent to the viewer when called
	*/
	gatherComments: function() {

		// make sure this only added when we're on a pattern specific view
		var body = document.getElementsByTagName("body");
		if (!body[0].classList.contains("sg-pattern-list")) {

			// count elements so it can be used when displaying the results in the viewer
			var count = 0;

			// iterate over the comments in annotations.js
			for(i = 0; i < comments.comments.length; i++) {

				var item = comments.comments[i];
				var els  = document.querySelectorAll(item.el);

				// if an element is found in the given pattern add it to the overall object so it can be passed when the overlay is turned on
				if (els.length > 0) {
					annotationsPattern.commentsGathered.comments[count] = { "el": item.el, "title": item.title, "comment": item.comment };
					count++;
				}

			}

			annotationsPattern.commentsGathered.count = count;

		} else {

			var obj = { "commentOverlay": "off" };
			var targetOrigin = (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host;
			parent.postMessage(obj,targetOrigin);

		}

	},

	/**
	* embed a comment by building the sg-annotations div (if necessary) and building an sg-annotation div
	* @param  {Object}      element to check the parent node of
	* @param  {String}      the title of the comment
	* @param  {String}      the comment HTML
	*/
	embedComments: function (el,title,comment) {

		// build the annotation div and add the content to it
		var annotationDiv = document.createElement("div");
		annotationDiv.classList.add("sg-annotation");

		var h3       = document.createElement("h3");
		var p        = document.createElement("p");
		h3.innerHTML = title;
		p.innerHTML  = comment;

		annotationDiv.appendChild(h3);
		annotationDiv.appendChild(p);

		// find the parent element to attach things to
		var parentEl = annotationsPattern.findParent(el);

		// see if a child with the class annotations exists
		var els = parentEl.getElementsByClassName("sg-annotations");
		if (els.length > 0) {
			els[0].appendChild(annotationDiv);
		} else {
			var annotationsDiv = document.createElement("div");
			annotationsDiv.classList.add("sg-annotations");
			annotationsDiv.appendChild(annotationDiv);
			parentEl.appendChild(annotationsDiv);
		}

	},

	/**
	* recursively find the parent of an element to see if it contains the sg-pattern class
	* @param  {Object}      element to check the parent node of
	*/
	findParent: function(el) {

		var parentEl;

		if (el.parentNode.classList.contains("sg-pattern")) {
			return el.parentNode;
		} else {
			parentEl = annotationsPattern.findParent(el.parentNode);
		}

		return parentEl;

	},

	/**
	* toggle the annotation feature on/off
	* based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage
	* @param  {Object}      event info
	*/
	receiveIframeMessage: function(event) {

		// does the origin sending the message match the current host? if not dev/null the request
		if ((window.location.protocol != "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) {
			return;
		}

		if (event.data.commentToggle !== undefined) {

			var i, els, item;

			// if this is an overlay make sure it's active for the onclick event
			annotationsPattern.commentsOverlayActive  = false;
			annotationsPattern.commentsEmbeddedActive = false;

			// see which flag to toggle based on if this is a styleguide or view-all page
			var body = document.getElementsByTagName("body");
			if ((event.data.commentToggle === "on") && (body[0].classList.contains("sg-pattern-list"))) {
				annotationsPattern.commentsEmbeddedActive = true;
			} else if (event.data.commentToggle === "on") {
				annotationsPattern.commentsOverlayActive  = true;
			}

			// if comments overlay is turned off make sure to remove the has-comment class and pointer
			if (!annotationsPattern.commentsOverlayActive) {
				els = document.querySelectorAll(".has-comment");
				for (i = 0; i < els.length; i++) {
					els[i].classList.remove("has-comment");
				}
			}

			// if comments embedding is turned off make sure to hide the annotations div
			if (!annotationsPattern.commentsEmbeddedActive) {
				els = document.getElementsByClassName("sg-annotations");
				for (i = 0; i < els.length; i++) {
					els[i].style.display = "none";
				}
			}

			// if comments overlay is turned on add the has-comment class and pointer
			if (annotationsPattern.commentsOverlayActive) {

				for (i = 0; i < comments.comments.length; i++) {
					item = comments.comments[i];
					els  = document.querySelectorAll(item.el);
					for (k = 0; k < els.length; k++) {
						els[k].classList.add("has-comment");
					}
				}

				// send the list of annotations for the page back to the parent
				var targetOrigin = (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host;
				parent.postMessage(annotationsPattern.commentsGathered,targetOrigin);

			} else if (annotationsPattern.commentsEmbeddedActive && !annotationsPattern.commentsEmbedded) {

				// if comment embedding is turned on and comments haven't been embedded yet do it
				for (i = 0; i < comments.comments.length; i++)  {
					item = comments.comments[i];
					els  = document.querySelectorAll(item.el);
					if (els.length > 0) {
						annotationsPattern.embedComments(els[0],item.title,item.comment); //Embed the comment
					}
					annotationsPattern.commentsEmbedded = true;
				}

			} else if (annotationsPattern.commentsEmbeddedActive && annotationsPattern.commentsEmbedded) {

				// if comment embedding is turned on and comments have been embedded simply display them
				els = document.getElementsByClassName("sg-annotations");
				for (i = 0; i < els.length; ++i) {
					els[i].style.display = "block";
				}

			}

		}

	}

};

// add the onclick handlers to the elements that have an annotations
annotationsPattern.gatherComments();
window.addEventListener("message", annotationsPattern.receiveIframeMessage, false);

// before unloading the iframe make sure any active overlay is turned off/closed
window.onbeforeunload = function() {
	var obj = { "commentOverlay": "off" };
	var targetOrigin = (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host;
	parent.postMessage(obj,targetOrigin);
};<commit_msg>first stab at annotation numbers<commit_after>/*!
 * Annotations Support for Patterns - v0.3
 *
 * Copyright (c) 2013 Dave Olsen, http://dmolsen.com
 * Licensed under the MIT license
 *
 */

var annotationsPattern = {

	commentsOverlayActive:  false,
	commentsOverlay:        false,
	commentsOverlayElement: "",
	commentsEmbeddedActive: false,
	commentsEmbedded:       false,
	commentsGathered:       { "commentOverlay": "on", "count": 0, "comments": { } },

	/**
	* record which annotations are related to this pattern so they can be sent to the viewer when called
	*/
	gatherComments: function() {

		// make sure this only added when we're on a pattern specific view
		var body = document.getElementsByTagName("body");
		if (!body[0].classList.contains("sg-pattern-list")) {

			// count elements so it can be used when displaying the results in the viewer
			var count = 0;

			// iterate over the comments in annotations.js
			for(i = 0; i < comments.comments.length; i++) {

				var item = comments.comments[i];
				var els  = document.querySelectorAll(item.el);

				// if an element is found in the given pattern add it to the overall object so it can be passed when the overlay is turned on
				if (els.length > 0) {

					annotationsPattern.commentsGathered.comments[count] = { "el": item.el, "title": item.title, "comment": item.comment };
					count++;

					var numberDiv = document.createElement("div");
					numberDiv.style.position   = "absolute";
					numberDiv.style.zIndex     = 99;
					numberDiv.style.marginLeft = -10;
					numberDiv.style.marginTop  = -10;
					numberDiv.innerHTML        = count;

					for (k = 0; k < els.length; k++) {
						els[k].appendChild(numberDiv);
					}

				}

			}

			annotationsPattern.commentsGathered.count = count;

		} else {

			var obj = { "commentOverlay": "off" };
			var targetOrigin = (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host;
			parent.postMessage(obj,targetOrigin);

		}

	},

	/**
	* embed a comment by building the sg-annotations div (if necessary) and building an sg-annotation div
	* @param  {Object}      element to check the parent node of
	* @param  {String}      the title of the comment
	* @param  {String}      the comment HTML
	*/
	embedComments: function (el,title,comment) {

		// build the annotation div and add the content to it
		var annotationDiv = document.createElement("div");
		annotationDiv.classList.add("sg-annotation");

		var h3       = document.createElement("h3");
		var p        = document.createElement("p");
		h3.innerHTML = title;
		p.innerHTML  = comment;

		annotationDiv.appendChild(h3);
		annotationDiv.appendChild(p);

		// find the parent element to attach things to
		var parentEl = annotationsPattern.findParent(el);

		// see if a child with the class annotations exists
		var els = parentEl.getElementsByClassName("sg-annotations");
		if (els.length > 0) {
			els[0].appendChild(annotationDiv);
		} else {
			var annotationsDiv = document.createElement("div");
			annotationsDiv.classList.add("sg-annotations");
			annotationsDiv.appendChild(annotationDiv);
			parentEl.appendChild(annotationsDiv);
		}

	},

	/**
	* recursively find the parent of an element to see if it contains the sg-pattern class
	* @param  {Object}      element to check the parent node of
	*/
	findParent: function(el) {

		var parentEl;

		if (el.parentNode.classList.contains("sg-pattern")) {
			return el.parentNode;
		} else {
			parentEl = annotationsPattern.findParent(el.parentNode);
		}

		return parentEl;

	},

	/**
	* toggle the annotation feature on/off
	* based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage
	* @param  {Object}      event info
	*/
	receiveIframeMessage: function(event) {

		// does the origin sending the message match the current host? if not dev/null the request
		if ((window.location.protocol != "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) {
			return;
		}

		if (event.data.commentToggle !== undefined) {

			var i, els, item;

			// if this is an overlay make sure it's active for the onclick event
			annotationsPattern.commentsOverlayActive  = false;
			annotationsPattern.commentsEmbeddedActive = false;

			// see which flag to toggle based on if this is a styleguide or view-all page
			var body = document.getElementsByTagName("body");
			if ((event.data.commentToggle === "on") && (body[0].classList.contains("sg-pattern-list"))) {
				annotationsPattern.commentsEmbeddedActive = true;
			} else if (event.data.commentToggle === "on") {
				annotationsPattern.commentsOverlayActive  = true;
			}

			// if comments overlay is turned off make sure to remove the has-comment class and pointer
			if (!annotationsPattern.commentsOverlayActive) {
				els = document.querySelectorAll(".has-comment");
				for (i = 0; i < els.length; i++) {
					els[i].classList.remove("has-comment");
				}
			}

			// if comments embedding is turned off make sure to hide the annotations div
			if (!annotationsPattern.commentsEmbeddedActive) {
				els = document.getElementsByClassName("sg-annotations");
				for (i = 0; i < els.length; i++) {
					els[i].style.display = "none";
				}
			}

			// if comments overlay is turned on add the has-comment class and pointer
			if (annotationsPattern.commentsOverlayActive) {

				for (i = 0; i < comments.comments.length; i++) {
					item = comments.comments[i];
					els  = document.querySelectorAll(item.el);
					for (k = 0; k < els.length; k++) {
						els[k].classList.add("has-comment");
					}
				}

				// send the list of annotations for the page back to the parent
				var targetOrigin = (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host;
				parent.postMessage(annotationsPattern.commentsGathered,targetOrigin);

			} else if (annotationsPattern.commentsEmbeddedActive && !annotationsPattern.commentsEmbedded) {

				// if comment embedding is turned on and comments haven't been embedded yet do it
				for (i = 0; i < comments.comments.length; i++)  {
					item = comments.comments[i];
					els  = document.querySelectorAll(item.el);
					if (els.length > 0) {
						annotationsPattern.embedComments(els[0],item.title,item.comment); //Embed the comment
					}
					annotationsPattern.commentsEmbedded = true;
				}

			} else if (annotationsPattern.commentsEmbeddedActive && annotationsPattern.commentsEmbedded) {

				// if comment embedding is turned on and comments have been embedded simply display them
				els = document.getElementsByClassName("sg-annotations");
				for (i = 0; i < els.length; ++i) {
					els[i].style.display = "block";
				}

			}

		}

	}

};

// add the onclick handlers to the elements that have an annotations
annotationsPattern.gatherComments();
window.addEventListener("message", annotationsPattern.receiveIframeMessage, false);

// before unloading the iframe make sure any active overlay is turned off/closed
window.onbeforeunload = function() {
	var obj = { "commentOverlay": "off" };
	var targetOrigin = (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host;
	parent.postMessage(obj,targetOrigin);
};<|endoftext|>


<commit_before>// COLORS
export const COLORS = {
  white: '#FFF',
  black: '#000'
};

export const PRIMARY_COLOR = {
  main: '#28292D',
  alt: '#2E2F33',
  icon: '#C2C2C2',
  text_main: COLORS.white,
  text_alt: '#EBEBEB'
};

export const SECONDARY_COLOR = {
  main: '#1CA6FC',
  alt: '#005FAF',
  icon: '#1CA6FC'
};
<commit_msg>added Google Material colors<commit_after>// COLORS
export const COLORS = {
  white: '#FFF',
  black: '#000'
};

export const MATERIAL_COLORS = {
  500: {
    red: '#F44336',
    pink: '#E91E63',
    purple: '#9C27B0',
    deep_purple: '#673AB7',
    indigo: '#3F51B5',
    blue: '#2196F3',
    light_blue: '#03A9F4',
    cyan: '#00BCD4',
    teal: '#009688',
    green: '#4CAF50',
    light_green: '#8BC34A',
    lime: '#CDDC39',
    yellow: '#FFEB3B',
    amber: '#FFC107',
    orange: '#FF9800',
    deep_orange: '#FF5722',
    brown: '#795548',
    grey: '#9E9E9E',
    blue_grey:'#607D8B'
  }
};

export const PRIMARY_COLOR = {
  main: '#28292D',
  alt: '#2E2F33',
  icon: '#C2C2C2',
  border: '1px solid #555',
  text_main: COLORS.white,
  text_alt: '#EBEBEB'
};

export const SECONDARY_COLOR = {
  main: '#1CA6FC',
  alt: '#005FAF',
  icon: '#1CA6FC',
  border: '1px solid #FFF'
};
<|endoftext|>


<commit_before>'use strict';

const retrieveBSON = require('./connection/utils').retrieveBSON;
const EventEmitter = require('events');
const BSON = retrieveBSON();
const Binary = BSON.Binary;
const uuidV4 = require('./utils').uuidV4;
const MongoError = require('./error').MongoError;

function assertAlive(session, callback) {
  if (session.serverSession == null) {
    const error = new MongoError('Cannot use a session that has ended');
    if (typeof callback === 'function') {
      return callback(error, null);
    }

    throw error;
  }
};

/** A class representing a client session on the server */
class ClientSession extends EventEmitter {

  /**
   * Create a client session.
   * WARNING: not meant to be instantiated directly
   *
   * @param {Topology} topology The current client's topology
   * @param {ServerSessionPool} sessionPool The server session pool
   * @param {Object} [options] Optional settings
   * @param {Boolean} [options.causalConsistency] Whether causal consistency should be enabled on this session
   * @param {Boolean} [options.autoStartTransaction=false] When enabled this session automatically starts a transaction with the provided defaultTransactionOptions.
   * @param {Object} [options.defaultTransactionOptions] The default TransactionOptions to use for transactions started on this session.
   */
  constructor(topology, sessionPool, options) {
    super();

    if (topology == null) {
      throw new Error('ClientSession requires a topology');
    }

    if (sessionPool == null || !(sessionPool instanceof ServerSessionPool)) {
      throw new Error('ClientSession requires a ServerSessionPool');
    }

    options = options || {};
    this.topology = topology;
    this.sessionPool = sessionPool;
    this.hasEnded = false;
    this.serverSession = sessionPool.acquire();

    this.supports = {
      causalConsistency: !!options.causalConsistency
    };

    options = options || {};
    if (typeof options.initialClusterTime !== 'undefined') {
      this.clusterTime = options.initialClusterTime;
    } else {
      this.clusterTime = null;
    }

    this.operationTime = null;

    this.explicit = !!options.explicit;
    this.owner = options.owner;
    this.transactionOptions = null;
    this.defaultTransactionOptions = options.defaultTransactionOptions || {};

    if (options.autoStartTransaction) {
      this.startTransaction();
    }
  }

  /**
   * Ends this session on the server
   *
   * @param {Object} [options] Optional settings
   * @param {Boolean} [options.skipCommand] Skip sending the actual endSessions command to the server
   * @param {Function} [callback] Optional callback for completion of this operation
   */
  endSession(options, callback) {
    if (typeof options === 'function') (callback = options), (options = {});
    options = options || {};

    if (this.hasEnded) {
      if (typeof callback === 'function') callback(null, null);
      return;
    }

    if (this.serverSession && this.inTransaction()) {
      this.abortTransaction();  // pass in callback?
    }

    if (!options.skipCommand) {
      // send the `endSessions` command
      this.topology.endSessions(this.id);
    }

    // mark the session as ended, and emit a signal
    this.hasEnded = true;
    this.emit('ended', this);

    // release the server session back to the pool
    this.sessionPool.release(this.serverSession);

    // spec indicates that we should ignore all errors for `endSessions`
    if (typeof callback === 'function') callback(null, null);
  }

  /**
   * Advances the operationTime for a ClientSession.
   *
   * @param {object} operationTime the `BSON.Timestamp` of the operation type it is desired to advance to
   */
  advanceOperationTime(operationTime) {
    if (this.operationTime == null) {
      this.operationTime = operationTime;
      return;
    }

    if (operationTime.greaterThan(this.operationTime)) {
      this.operationTime = operationTime;
    }
  }

  /**
   * Used to determine if this session equals another
   */
  equals(session) {
    if (!(session instanceof ClientSession)) {
      return false;
    }

    return this.id.id.buffer.equals(session.id.id.buffer);
  }

  /**
   * @returns whether this session is current in a transaction or not
   */
  inTransaction() {
    return this.transactionOptions != null;
  }

  /**
   * Starts a new transaction with the given options.
   *
   * @param {Object} options Optional settings
   * @param {ReadConcern} [options.readConcern] The readConcern to use for this transaction
   * @param {WriteConcern} [options.writeConcern] The writeConcern to use for this transaction
   */
  startTransaction(options) {
    assertAlive(this);
    if (this.inTransaction()) {
      throw new MongoError('Transaction already started');
    }

    // increment txnNumber and reset stmtId to zero.
    this.serverSession.txnNumber += 1;
    this.serverSession.stmtId = 0;

    // set transaction options, we will use this to determine if we are in a transaction
    this.transactionOptions = options || this.defaultTransactionOptions;
  }

  /**
   * Commits the currently active transaction in this session.
   *
   * @param {Function} [callback] optional callback for completion of this operation
   * @return {Promise} A promise is returned if no callback is provided
   */
  commitTransaction(callback) {
    if (typeof callback === 'function') {
      endTransaction(this, 'commitTransaction', callback);
      return;
    }

    return new Promise((resolve, reject) => {
      endTransaction(this, 'commitTransaction', (err, reply) => err ? reject(err) : resolve(reply));
    });
  }

  /**
   * Aborts the currently active transaction in this session.
   *
   * @param {Function} [callback] optional callback for completion of this operation
   * @return {Promise} A promise is returned if no callback is provided
   */
  abortTransaction(callback) {
    if (typeof callback === 'function') {
      endTransaction(this, 'abortTransaction', callback);
      return;
    }

    return new Promise((resolve, reject) => {
      endTransaction(this, 'abortTransaction', (err, reply) => err ? reject(err) : resolve(reply));
    });

  }
}

function endTransaction(clientSession, commandName, callback) {
  assertAlive(clientSession, callback);

  if (!clientSession.inTransaction()) {
    callback(new MongoError('No transaction started'));
    return;
  }

  if (clientSession.serverSession.stmtId === 0) {
    // The server transaction was never started.
    callback(null, null);
    return;
  }

  // send the command
  clientSession.topology.command('admin.$cmd', { [commandName]: 1 }, {
    writeConcern: clientSession.transactionOptions.writeConcern
  }, (err, reply) => {
    // reset internal transaction state
    if (clientSession.options.autoStartTransaction) {
      clientSession.startTransaction();
    } else {
      clientSession.transactionOptions = null;
    }

    callback(err, reply);
  });
}

Object.defineProperty(ClientSession.prototype, 'id', {
  get: function() {
    return this.serverSession.id;
  }
});

/**
 *
 */
class ServerSession {
  constructor() {
    this.id = { id: new Binary(uuidV4(), Binary.SUBTYPE_UUID) };
    this.lastUse = Date.now();
    this.txnNumber = 0;
  }

  /**
   *
   * @param {*} sessionTimeoutMinutes
   */
  hasTimedOut(sessionTimeoutMinutes) {
    // Take the difference of the lastUse timestamp and now, which will result in a value in
    // milliseconds, and then convert milliseconds to minutes to compare to `sessionTimeoutMinutes`
    const idleTimeMinutes = Math.round(
      (((Date.now() - this.lastUse) % 86400000) % 3600000) / 60000
    );

    return idleTimeMinutes > sessionTimeoutMinutes - 1;
  }
}

/**
 *
 */
class ServerSessionPool {
  constructor(topology) {
    if (topology == null) {
      throw new Error('ServerSessionPool requires a topology');
    }

    this.topology = topology;
    this.sessions = [];
  }

  endAllPooledSessions() {
    if (this.sessions.length) {
      this.topology.endSessions(this.sessions.map(session => session.id));
      this.sessions = [];
    }
  }

  /**
   * @returns {ServerSession}
   */
  acquire() {
    const sessionTimeoutMinutes = this.topology.logicalSessionTimeoutMinutes;
    while (this.sessions.length) {
      const session = this.sessions.shift();
      if (!session.hasTimedOut(sessionTimeoutMinutes)) {
        return session;
      }
    }

    return new ServerSession();
  }

  /**
   *
   * @param {*} session
   */
  release(session) {
    const sessionTimeoutMinutes = this.topology.logicalSessionTimeoutMinutes;
    while (this.sessions.length) {
      const session = this.sessions[this.sessions.length - 1];
      if (session.hasTimedOut(sessionTimeoutMinutes)) {
        this.sessions.pop();
      } else {
        break;
      }
    }

    if (!session.hasTimedOut(sessionTimeoutMinutes)) {
      this.sessions.unshift(session);
    }
  }
}

module.exports = {
  ClientSession: ClientSession,
  ServerSession: ServerSession,
  ServerSessionPool: ServerSessionPool
};
<commit_msg>refactor(sessions): support autostarting sessions properly<commit_after>'use strict';

const retrieveBSON = require('./connection/utils').retrieveBSON;
const EventEmitter = require('events');
const BSON = retrieveBSON();
const Binary = BSON.Binary;
const Long = BSON.Long;
const uuidV4 = require('./utils').uuidV4;
const MongoError = require('./error').MongoError;

function assertAlive(session, callback) {
  if (session.serverSession == null) {
    const error = new MongoError('Cannot use a session that has ended');
    if (typeof callback === 'function') {
      return callback(error, null);
    }

    throw error;
  }
};

/** A class representing a client session on the server */
class ClientSession extends EventEmitter {

  /**
   * Create a client session.
   * WARNING: not meant to be instantiated directly
   *
   * @param {Topology} topology The current client's topology
   * @param {ServerSessionPool} sessionPool The server session pool
   * @param {Object} [options] Optional settings
   * @param {Boolean} [options.causalConsistency] Whether causal consistency should be enabled on this session
   * @param {Boolean} [options.autoStartTransaction=false] When enabled this session automatically starts a transaction with the provided defaultTransactionOptions.
   * @param {Object} [options.defaultTransactionOptions] The default TransactionOptions to use for transactions started on this session.
   */
  constructor(topology, sessionPool, options) {
    super();

    if (topology == null) {
      throw new Error('ClientSession requires a topology');
    }

    if (sessionPool == null || !(sessionPool instanceof ServerSessionPool)) {
      throw new Error('ClientSession requires a ServerSessionPool');
    }

    options = options || {};
    this.topology = topology;
    this.sessionPool = sessionPool;
    this.hasEnded = false;
    this.serverSession = sessionPool.acquire();

    this.supports = {
      causalConsistency: typeof options.causalConsistency !== 'undefined' ? options.causalConsistency : true
    };

    options = options || {};
    if (typeof options.initialClusterTime !== 'undefined') {
      this.clusterTime = options.initialClusterTime;
    } else {
      this.clusterTime = null;
    }

    this.operationTime = null;
    this.explicit = !!options.explicit;
    this.owner = options.owner;
    this.transactionOptions = null;
    this.defaultTransactionOptions = options.defaultTransactionOptions || {};
    this.autoStartTransaction = options.autoStartTransaction;

    // immediately start the transaction if autoStart has been chosen
    if (this.autoStartTransaction) {
      this.startTransaction();
    }
  }

  /**
   * Ends this session on the server
   *
   * @param {Object} [options] Optional settings
   * @param {Boolean} [options.skipCommand] Skip sending the actual endSessions command to the server
   * @param {Function} [callback] Optional callback for completion of this operation
   */
  endSession(options, callback) {
    if (typeof options === 'function') (callback = options), (options = {});
    options = options || {};

    if (this.hasEnded) {
      if (typeof callback === 'function') callback(null, null);
      return;
    }

    if (this.serverSession && this.inTransaction()) {
      this.abortTransaction();  // pass in callback?
    }

    if (!options.skipCommand) {
      // send the `endSessions` command
      this.topology.endSessions(this.id);
    }

    // mark the session as ended, and emit a signal
    this.hasEnded = true;
    this.emit('ended', this);

    // release the server session back to the pool
    this.sessionPool.release(this.serverSession);

    // spec indicates that we should ignore all errors for `endSessions`
    if (typeof callback === 'function') callback(null, null);
  }

  /**
   * Advances the operationTime for a ClientSession.
   *
   * @param {object} operationTime the `BSON.Timestamp` of the operation type it is desired to advance to
   */
  advanceOperationTime(operationTime) {
    if (this.operationTime == null) {
      this.operationTime = operationTime;
      return;
    }

    if (operationTime.greaterThan(this.operationTime)) {
      this.operationTime = operationTime;
    }
  }

  /**
   * Used to determine if this session equals another
   */
  equals(session) {
    if (!(session instanceof ClientSession)) {
      return false;
    }

    return this.id.id.buffer.equals(session.id.id.buffer);
  }

  /**
   * @returns whether this session is current in a transaction or not
   */
  inTransaction() {
    return this.transactionOptions != null;
  }

  /**
   * Starts a new transaction with the given options.
   *
   * @param {Object} options Optional settings
   * @param {ReadConcern} [options.readConcern] The readConcern to use for this transaction
   * @param {WriteConcern} [options.writeConcern] The writeConcern to use for this transaction
   */
  startTransaction(options) {
    assertAlive(this);
    if (this.inTransaction()) {
      throw new MongoError('Transaction already started');
    }

    // increment txnNumber and reset stmtId to zero.
    this.serverSession.txnNumber += 1;
    this.serverSession.stmtId = 0;

    // set transaction options, we will use this to determine if we are in a transaction
    this.transactionOptions = options || this.defaultTransactionOptions;
  }

  /**
   * Commits the currently active transaction in this session.
   *
   * @param {Function} [callback] optional callback for completion of this operation
   * @return {Promise} A promise is returned if no callback is provided
   */
  commitTransaction(callback) {
    if (typeof callback === 'function') {
      endTransaction(this, 'commitTransaction', callback);
      return;
    }

    return new Promise((resolve, reject) => {
      endTransaction(this, 'commitTransaction', (err, reply) => err ? reject(err) : resolve(reply));
    });
  }

  /**
   * Aborts the currently active transaction in this session.
   *
   * @param {Function} [callback] optional callback for completion of this operation
   * @return {Promise} A promise is returned if no callback is provided
   */
  abortTransaction(callback) {
    if (typeof callback === 'function') {
      endTransaction(this, 'abortTransaction', callback);
      return;
    }

    return new Promise((resolve, reject) => {
      endTransaction(this, 'abortTransaction', (err, reply) => err ? reject(err) : resolve(reply));
    });

  }
}

function endTransaction(clientSession, commandName, callback) {
  assertAlive(clientSession, callback);

  if (!clientSession.inTransaction()) {
    callback(new MongoError('No transaction started'));
    return;
  }

  if (clientSession.serverSession.stmtId === 0) {
    // The server transaction was never started.
    callback(null, null);
    return;
  }

  // send the command
  clientSession.topology.command('admin.$cmd', { [commandName]: 1 }, {
    writeConcern: clientSession.transactionOptions.writeConcern,
    session: clientSession
  }, (err, reply) => {
    // reset internal transaction state
    if (clientSession.autoStartTransaction) {
      clientSession.startTransaction();
    } else {
      clientSession.transactionOptions = null;
    }

    callback(err, reply);
  });
}

Object.defineProperty(ClientSession.prototype, 'id', {
  get: function() {
    return this.serverSession.id;
  }
});

/**
 *
 */
class ServerSession {
  constructor() {
    this.id = { id: new Binary(uuidV4(), Binary.SUBTYPE_UUID) };
    this.lastUse = Date.now();
    this.txnNumber = 0;
  }

  /**
   *
   * @param {*} sessionTimeoutMinutes
   */
  hasTimedOut(sessionTimeoutMinutes) {
    // Take the difference of the lastUse timestamp and now, which will result in a value in
    // milliseconds, and then convert milliseconds to minutes to compare to `sessionTimeoutMinutes`
    const idleTimeMinutes = Math.round(
      (((Date.now() - this.lastUse) % 86400000) % 3600000) / 60000
    );

    return idleTimeMinutes > sessionTimeoutMinutes - 1;
  }
}

/**
 *
 */
class ServerSessionPool {
  constructor(topology) {
    if (topology == null) {
      throw new Error('ServerSessionPool requires a topology');
    }

    this.topology = topology;
    this.sessions = [];
  }

  endAllPooledSessions() {
    if (this.sessions.length) {
      this.topology.endSessions(this.sessions.map(session => session.id));
      this.sessions = [];
    }
  }

  /**
   * @returns {ServerSession}
   */
  acquire() {
    const sessionTimeoutMinutes = this.topology.logicalSessionTimeoutMinutes;
    while (this.sessions.length) {
      const session = this.sessions.shift();
      if (!session.hasTimedOut(sessionTimeoutMinutes)) {
        return session;
      }
    }

    return new ServerSession();
  }

  /**
   *
   * @param {*} session
   */
  release(session) {
    const sessionTimeoutMinutes = this.topology.logicalSessionTimeoutMinutes;
    while (this.sessions.length) {
      const session = this.sessions[this.sessions.length - 1];
      if (session.hasTimedOut(sessionTimeoutMinutes)) {
        this.sessions.pop();
      } else {
        break;
      }
    }

    if (!session.hasTimedOut(sessionTimeoutMinutes)) {
      this.sessions.unshift(session);
    }
  }
}

module.exports = {
  ClientSession: ClientSession,
  ServerSession: ServerSession,
  ServerSessionPool: ServerSessionPool
};
<|endoftext|>


<commit_before>(function(webtorrent) {
	"use strict";

	var converts = [
		{ // Video
			from: /(.+)<\/a>/g,
			to: '
' +
			'<button class="btn btn-default" id="nodebb-webtorrent-loadbtn" onclick="nodebbwebtorrentload()">Load in Browser.</button>' +
			'<button class="btn btn-default">Download Magnet.</button>' +
			'
' +
			'
$2
'
		}
	];

	webtorrent.parse = function(data, callback) {
		//console.log(data.postData.content);
		//var magnetUrl;
		try {
			for(var i = 0; i < converts.length; i++) {
				data.postData.content = data.postData.content.replace(converts[i].from, converts[i].to);
			}
			callback(null, data);
		} catch(ex) {
			callback(ex, data);
		}
	};

	webtorrent.addScripts = function (scripts, callback){
		//console.log("o.o...");
		scripts.push('/assets/src/webtorrent.js');

		callback(null,scripts);
	}
})(module.exports);<commit_msg>fix bug: download magnet url button not add tracker server in tracker server list<commit_after>(function(webtorrent) {
	"use strict";

	var converts = [
		{ // Video
			from: /(.+)<\/a>/g,
			to: '
' +
			'<button class="btn btn-default" id="nodebb-webtorrent-loadbtn" onclick="nodebbwebtorrentload()">Load in Browser.</button>' +
			'<button class="btn btn-default" onclick="window.open(fixTorrent(\'$2\')[0]);">Download Magnet.</button>' +
			'
' +
			'
$2
'
		}
	];

	webtorrent.parse = function(data, callback) {
		//console.log(data.postData.content);
		//var magnetUrl;
		try {
			for(var i = 0; i < converts.length; i++) {
				data.postData.content = data.postData.content.replace(converts[i].from, converts[i].to);
			}
			callback(null, data);
		} catch(ex) {
			callback(ex, data);
		}
	};

	webtorrent.addScripts = function (scripts, callback){
		//console.log("o.o...");
		scripts.push('/assets/src/webtorrent.js');

		callback(null,scripts);
	}
})(module.exports);<|endoftext|>


<commit_before>var fs = require('fs');
var TableEditor = require('table-editor');
var prettify = require('jsonpretty');
var elClass = require('element-class');
var domready = require('domready');
var levelup = require('levelup');
var leveljs = require('level-js');
var on = require('dom-event');
var remove = require('remove-element');
var closest = require('component-closest');
var menuToggle = require('./lib/menu-toggle');
var keys = require('lodash.keys');
var flat = require('flat');

var io = require('socket.io-client')('http://flatsheet-realtime.herokuapp.com');

var user = {
  cell: null
};

io.on('connect', function(s){
  console.log('connection.');
});

io.on('cell-focus', function (cell) {
  elClass(document.getElementById(cell.id)).add('active');
});

io.on('change', function (data) {
  console.log(flat.flatten(data));
  editor.set(data);
});

io.on('cell-blur', function (cell) {
  elClass(document.getElementById(cell.id)).remove('active');
});

io.on('disconnect', function(){
  console.log('disconnection.');
});

/* get the table template */
var tableTemplate = fs.readFileSync('./templates/table.html', 'utf8');

/* create the table editor */
window.editor = new TableEditor('main-content', { headers: [], rows: [] }, tableTemplate);

editor.on('change', function (data) {
  io.emit('change', flat.flatten(data));
});

/* get the help message */
var hello = document.getElementById('hello-message');

/* created the db */
window.db = levelup('sheet', { db: leveljs, valueEncoding: 'json' });

/* check to see if the sheet has has been added to the db already */
db.get('sheet', function (err, value) {
  if (err) return console.error(err);
  if (value.headers.length > 0) {
    elClass(hello).add('hidden');
    editor.set(value);
  }
});

/* listen for changes to the data and save the object to the db */
editor.on('change', function (change, data) {
  db.put('sheet', data, function (error) {
    if (error) console.error(error);
  });
});

/* button element and listener for adding a row */
var addRow = document.getElementById('add-row');

on(addRow, 'click', function (e) {
  editor.addRow();
});

/* button element and listener for adding a column */
var addColumn = document.getElementById('add-column');

on(addColumn, 'click', function (e) {
  if (editor.data.headers.length < 1) elClass(hello).add('hidden');
  if (editor.data.rows < 1) editor.addRow();
  var name = window.prompt('New column name');
  if (name) editor.addColumn({ name: name, type: 'string' });
});

/* get elements for codebox and its textarea */
var codeBox = document.getElementById('code-box');
var textarea = codeBox.querySelector('textarea');

/* button element and listener for showing the data as json */
var showJSON = document.getElementById('show-json');

on(showJSON, 'click', function (e) {
  editor.getJSON(function (data) {
    textarea.value = prettify(data);
    elClass(codeBox).remove('hidden');
  });
});

/* button element and listener for showing the data as csv */
var showCSV = document.getElementById('show-csv');

on(showCSV, 'click', function (e) {
  editor.getCSV(function (data) {
    textarea.value = data;
    elClass(codeBox).remove('hidden');
  });
});

/* button element and listener for closing the codebox */
var close = document.getElementById('close');

on(close, 'click', function (e) {
  textarea.value = '';
  elClass(codeBox).add('hidden');
});

/* button element and listener for clearing the db */
var reset = document.getElementById('reset');

on(reset, 'click', function (e) {
  var msg = 'Are you sure you want to reset this project? You will start over with an empty workspace.';
  if (window.confirm(msg)) {
    editor.reset();
    elClass(hello).remove('hidden');
  };
});

/* element and listener for the table header */
var tableHeader = document.getElementById('table-header');

on(tableHeader, 'click', function (e) {
  if (elClass(e.target).has('setting')) {
    var btn = e.target.id.split('-');

    if (btn[0] === 'delete') {
      if (window.confirm('Sure you want to delete this column and its contents?')) {
        editor.deleteColumn(btn[1]);
      }
    }

    if (btn[0] === 'rename') {
      var newName = window.prompt('Choose a new column name:')
      if (newName) editor.renameColumn(btn[1], newName);
    }
  }

  else menuToggle('header', e.target)
});

/* element and listener for the table body */
var tableBody = document.getElementById('table-body');

on(tableBody, 'click', function (e) {
  var btn;

  if (e.target.tagName === 'TEXTAREA') {
    var cellEl = document.getElementById(closest(e.target, 'td').id);

    user.cell = {
      id: closest(e.target, 'td').id,
      value: e.target.value
    }

    io.emit('cell-focus', user.cell);

    on(e.target, 'blur', function (blurEvent) {
      io.emit('cell-blur', user.cell);
    });

    return;
  }

  else if (elClass(e.target).has('delete-row')) btn = e.target;
  else if (elClass(e.target).has('delete-btn-icon')) btn = closest(e.target, '.delete-row');
  else return;

  if (window.confirm('Sure you want to delete this row and its contents?')) {
    var row = closest(btn, 'tr');
    editor.deleteRow(row.className);
  }
});
<commit_msg>fix editor on change event listener<commit_after>var fs = require('fs');
var TableEditor = require('table-editor');
var prettify = require('jsonpretty');
var elClass = require('element-class');
var domready = require('domready');
var levelup = require('levelup');
var leveljs = require('level-js');
var on = require('dom-event');
var remove = require('remove-element');
var closest = require('component-closest');
var menuToggle = require('./lib/menu-toggle');
var keys = require('lodash.keys');
var flat = require('flat');

var io = require('socket.io-client')('http://flatsheet-realtime.herokuapp.com');

var user = {
  cell: null
};

io.on('connect', function(s){
  console.log('connection.');
});

io.on('cell-focus', function (cell) {
  elClass(document.getElementById(cell.id)).add('active');
});

io.on('change', function (data) {
  console.log(flat.flatten(data));
  editor.set(data);
});

io.on('cell-blur', function (cell) {
  elClass(document.getElementById(cell.id)).remove('active');
});

io.on('disconnect', function(){
  console.log('disconnection.');
});

/* get the table template */
var tableTemplate = fs.readFileSync('./templates/table.html', 'utf8');

/* create the table editor */
window.editor = new TableEditor('main-content', { headers: [], rows: [] }, tableTemplate);

editor.on('change', function (data) {

});

/* get the help message */
var hello = document.getElementById('hello-message');

/* created the db */
window.db = levelup('sheet', { db: leveljs, valueEncoding: 'json' });

/* check to see if the sheet has has been added to the db already */
db.get('sheet', function (err, value) {
  if (err) return console.error(err);
  if (value.headers.length > 0) {
    elClass(hello).add('hidden');
    editor.set(value);
  }
});

/* listen for changes to the data and save the object to the db */
editor.on('change', function (data) {
  db.put('sheet', data, function (error) {
    if (error) console.error(error);
  });
  io.emit('change', flat.flatten(data));
});

/* button element and listener for adding a row */
var addRow = document.getElementById('add-row');

on(addRow, 'click', function (e) {
  editor.addRow();
});

/* button element and listener for adding a column */
var addColumn = document.getElementById('add-column');

on(addColumn, 'click', function (e) {
  if (editor.data.headers.length < 1) elClass(hello).add('hidden');
  if (editor.data.rows < 1) editor.addRow();
  var name = window.prompt('New column name');
  if (name) editor.addColumn({ name: name, type: 'string' });
});

/* get elements for codebox and its textarea */
var codeBox = document.getElementById('code-box');
var textarea = codeBox.querySelector('textarea');

/* button element and listener for showing the data as json */
var showJSON = document.getElementById('show-json');

on(showJSON, 'click', function (e) {
  editor.getJSON(function (data) {
    textarea.value = prettify(data);
    elClass(codeBox).remove('hidden');
  });
});

/* button element and listener for showing the data as csv */
var showCSV = document.getElementById('show-csv');

on(showCSV, 'click', function (e) {
  editor.getCSV(function (data) {
    textarea.value = data;
    elClass(codeBox).remove('hidden');
  });
});

/* button element and listener for closing the codebox */
var close = document.getElementById('close');

on(close, 'click', function (e) {
  textarea.value = '';
  elClass(codeBox).add('hidden');
});

/* button element and listener for clearing the db */
var reset = document.getElementById('reset');

on(reset, 'click', function (e) {
  var msg = 'Are you sure you want to reset this project? You will start over with an empty workspace.';
  if (window.confirm(msg)) {
    editor.reset();
    elClass(hello).remove('hidden');
  };
});

/* element and listener for the table header */
var tableHeader = document.getElementById('table-header');

on(tableHeader, 'click', function (e) {
  if (elClass(e.target).has('setting')) {
    var btn = e.target.id.split('-');

    if (btn[0] === 'delete') {
      if (window.confirm('Sure you want to delete this column and its contents?')) {
        editor.deleteColumn(btn[1]);
      }
    }

    if (btn[0] === 'rename') {
      var newName = window.prompt('Choose a new column name:')
      if (newName) editor.renameColumn(btn[1], newName);
    }
  }

  else menuToggle('header', e.target)
});

/* element and listener for the table body */
var tableBody = document.getElementById('table-body');

on(tableBody, 'click', function (e) {
  var btn;

  if (e.target.tagName === 'TEXTAREA') {
    var cellEl = document.getElementById(closest(e.target, 'td').id);

    user.cell = {
      id: closest(e.target, 'td').id,
      value: e.target.value
    }

    io.emit('cell-focus', user.cell);

    on(e.target, 'blur', function (blurEvent) {
      io.emit('cell-blur', user.cell);
    });

    return;
  }

  else if (elClass(e.target).has('delete-row')) btn = e.target;
  else if (elClass(e.target).has('delete-btn-icon')) btn = closest(e.target, '.delete-row');
  else return;

  if (window.confirm('Sure you want to delete this row and its contents?')) {
    var row = closest(btn, 'tr');
    editor.deleteRow(row.className);
  }
});
<|endoftext|>


<commit_before>/*!
 * Annotations Support for Patterns - v0.3
 *
 * Copyright (c) 2013-2014 Dave Olsen, http://dmolsen.com
 * Licensed under the MIT license
 *
 */

var annotationsPattern = {

	commentsOverlayActive:  false,
	commentsOverlay:        false,
	commentsOverlayElement: "",
	commentsEmbeddedActive: false,
	commentsEmbedded:       false,
	commentsGathered:       { "commentOverlay": "on", "count": 0, "comments": { } },

	/**
	* record which annotations are related to this pattern so they can be sent to the viewer when called
	*/
	gatherComments: function() {

		// make sure this only added when we're on a pattern specific view
		if (document.getElementById("sg-patterns") === null) {

		} else {

			var obj = { "commentOverlay": "off" };
			var targetOrigin = (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host;
			parent.postMessage(obj,targetOrigin);

		}

	},

	/**
	* embed a comment by building the sg-annotations div (if necessary) and building an sg-annotation div
	* @param  {Object}      element to check the parent node of
	* @param  {String}      the title of the comment
	* @param  {String}      the comment HTML
	*/
	embedComments: function (el,title,comment) {

		// build the annotation div and add the content to it
		var annotationDiv = document.createElement("div");
		annotationDiv.classList.add("sg-annotation");

		var h3       = document.createElement("h3");
		var p        = document.createElement("p");
		h3.innerHTML = title;
		p.innerHTML  = comment;

		annotationDiv.appendChild(h3);
		annotationDiv.appendChild(p);

		// find the parent element to attach things to
		var parentEl = annotationsPattern.findParent(el);

		// see if a child with the class annotations exists
		var els = parentEl.getElementsByClassName("sg-annotations");
		if (els.length > 0) {
			els[0].appendChild(annotationDiv);
		} else {
			var annotationsDiv = document.createElement("div");
			annotationsDiv.classList.add("sg-annotations");
			annotationsDiv.appendChild(annotationDiv);
			parentEl.appendChild(annotationsDiv);
		}

	},

	/**
	* recursively find the parent of an element to see if it contains the sg-pattern class
	* @param  {Object}      element to check the parent node of
	*/
	findParent: function(el) {

		var parentEl;

		if (el.parentNode.classList.contains("sg-pattern")) {
			return el.parentNode;
		} else {
			parentEl = annotationsPattern.findParent(el.parentNode);
		}

		return parentEl;

	},

	/**
	* toggle the annotation feature on/off
	* based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage
	* @param  {Object}      event info
	*/
	receiveIframeMessage: function(event) {

		// does the origin sending the message match the current host? if not dev/null the request
		if ((window.location.protocol != "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) {
			return;
		}

		if (event.data.commentToggle !== undefined) {

			var i, els, item, displayNum;

			// if this is an overlay make sure it's active for the onclick event
			annotationsPattern.commentsOverlayActive  = false;
			annotationsPattern.commentsEmbeddedActive = false;

			// see which flag to toggle based on if this is a styleguide or view-all page
			if ((event.data.commentToggle === "on") && (document.getElementById("sg-patterns") !== null)) {
				annotationsPattern.commentsEmbeddedActive = true;
			} else if (event.data.commentToggle === "on") {
				annotationsPattern.commentsOverlayActive  = true;
			}

			// if comments overlay is turned off make sure to remove the has-annotation class and pointer
			if (!annotationsPattern.commentsOverlayActive) {
				els = document.querySelectorAll(".has-annotation");
				for (i = 0; i < els.length; i++) {
					els[i].classList.remove("has-annotation");

				}
			}

			// if comments embedding is turned off make sure to hide the annotations div
			if (!annotationsPattern.commentsEmbeddedActive) {
				els = document.getElementsByClassName("sg-annotations");
				for (i = 0; i < els.length; i++) {
					els[i].style.display = "none";
				}
			}

			// if comments overlay is turned on add the has-annotation class and pointer
			if (annotationsPattern.commentsOverlayActive) {

				for (i = 0; i < comments.comments.length; i++) {
					item = comments.comments[i];
					els  = document.querySelectorAll(item.el),
					displayNum = i+1;

					//Loop through all items with annotations
					for (k = 0; k < els.length; k++) {
						els[k].classList.add("has-annotation");

						var numberDiv = document.createElement("a");
						numberDiv.href = "#annotation-" + displayNum;
						numberDiv.classList.add("annotation-tip");

						els[k].appendChild(numberDiv);
						numberDiv.innerHTML = displayNum;
					}
				}

			// count elements so it can be used when displaying the results in the viewer
			var count = 0;

			// iterate over the comments in annotations.js
			for(i = 0; i < comments.comments.length; i++) {

				var item = comments.comments[i];
				var els  = document.querySelectorAll(item.el);

				// if an element is found in the given pattern add it to the overall object so it can be passed when the overlay is turned on
				if (els.length > 0) {

					annotationsPattern.commentsGathered.comments[count] = { "el": item.el, "title": item.title, "comment": item.comment };
					count++;
				}

			}

			annotationsPattern.commentsGathered.count = count;

				// send the list of annotations for the page back to the parent
				var targetOrigin = (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host;
				parent.postMessage(annotationsPattern.commentsGathered,targetOrigin);

			} else if (annotationsPattern.commentsEmbeddedActive && !annotationsPattern.commentsEmbedded) {

				// if comment embedding is turned on and comments haven't been embedded yet do it
				for (i = 0; i < comments.comments.length; i++)  {
					item = comments.comments[i];
					els  = document.querySelectorAll(item.el);
					if (els.length > 0) {
						annotationsPattern.embedComments(els[0],item.title,item.comment); //Embed the comment
					}
					annotationsPattern.commentsEmbedded = true;
				}

			} else if (annotationsPattern.commentsEmbeddedActive && annotationsPattern.commentsEmbedded) {

				// if comment embedding is turned on and comments have been embedded simply display them
				els = document.getElementsByClassName("sg-annotations");
				for (i = 0; i < els.length; ++i) {
					els[i].style.display = "block";
				}

			}

		}

	}

};

// add the onclick handlers to the elements that have an annotations
annotationsPattern.gatherComments();
window.addEventListener("message", annotationsPattern.receiveIframeMessage, false);

// before unloading the iframe make sure any active overlay is turned off/closed
window.onbeforeunload = function() {
	var obj = { "commentOverlay": "off" };
	var targetOrigin = (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host;
	parent.postMessage(obj,targetOrigin);
};<commit_msg>annotation code clean-up<commit_after>/*!
 * Annotations Support for Patterns - v0.3
 *
 * Copyright (c) 2013-2014 Dave Olsen, http://dmolsen.com
 * Licensed under the MIT license
 *
 */

var annotationsPattern = {

	commentsOverlayActive:  false,
	commentsOverlay:        false,
	commentsOverlayElement: "",
	commentsEmbeddedActive: false,
	commentsEmbedded:       false,
	commentsGathered:       { "commentOverlay": "on", "count": 0, "comments": { } },

	/**
	* record which annotations are related to this pattern so they can be sent to the viewer when called
	*/
	gatherComments: function() {

		// make sure this only added when we're on a pattern specific view
		if (document.getElementById("sg-patterns") !== null) {

			var obj = { "commentOverlay": "off" };
			var targetOrigin = (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host;
			parent.postMessage(obj,targetOrigin);

		}

	},

	/**
	* embed a comment by building the sg-annotations div (if necessary) and building an sg-annotation div
	* @param  {Object}      element to check the parent node of
	* @param  {String}      the title of the comment
	* @param  {String}      the comment HTML
	*/
	embedComments: function (el,title,comment) {

		// build the annotation div and add the content to it
		var annotationDiv = document.createElement("div");
		annotationDiv.classList.add("sg-annotation");

		var h3       = document.createElement("h3");
		var p        = document.createElement("p");
		h3.innerHTML = title;
		p.innerHTML  = comment;

		annotationDiv.appendChild(h3);
		annotationDiv.appendChild(p);

		// find the parent element to attach things to
		var parentEl = annotationsPattern.findParent(el);

		// see if a child with the class annotations exists
		var els = parentEl.getElementsByClassName("sg-annotations");
		if (els.length > 0) {
			els[0].appendChild(annotationDiv);
		} else {
			var annotationsDiv = document.createElement("div");
			annotationsDiv.classList.add("sg-annotations");
			annotationsDiv.appendChild(annotationDiv);
			parentEl.appendChild(annotationsDiv);
		}

	},

	/**
	* recursively find the parent of an element to see if it contains the sg-pattern class
	* @param  {Object}      element to check the parent node of
	*/
	findParent: function(el) {

		var parentEl;

		if (el.parentNode.classList.contains("sg-pattern")) {
			return el.parentNode;
		} else {
			parentEl = annotationsPattern.findParent(el.parentNode);
		}

		return parentEl;

	},

	/**
	* toggle the annotation feature on/off
	* based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage
	* @param  {Object}      event info
	*/
	receiveIframeMessage: function(event) {

		// does the origin sending the message match the current host? if not dev/null the request
		if ((window.location.protocol != "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) {
			return;
		}

		if (event.data.commentToggle !== undefined) {

			var i, els, item, displayNum;

			// if this is an overlay make sure it's active for the onclick event
			annotationsPattern.commentsOverlayActive  = false;
			annotationsPattern.commentsEmbeddedActive = false;

			// see which flag to toggle based on if this is a styleguide or view-all page
			if ((event.data.commentToggle === "on") && (document.getElementById("sg-patterns") !== null)) {
				annotationsPattern.commentsEmbeddedActive = true;
			} else if (event.data.commentToggle === "on") {
				annotationsPattern.commentsOverlayActive  = true;
			}

			// if comments overlay is turned off make sure to remove the has-annotation class and pointer
			if (!annotationsPattern.commentsOverlayActive) {
				els = document.querySelectorAll(".has-annotation");
				for (i = 0; i < els.length; i++) {
					els[i].classList.remove("has-annotation");
				}
			}

			// if comments embedding is turned off make sure to hide the annotations div
			if (!annotationsPattern.commentsEmbeddedActive) {
				els = document.getElementsByClassName("sg-annotations");
				for (i = 0; i < els.length; i++) {
					els[i].style.display = "none";
				}
			}

			// if comments overlay is turned on add the has-annotation class and pointer
			if (annotationsPattern.commentsOverlayActive) {

				for (i = 0; i < comments.comments.length; i++) {
					item = comments.comments[i];
					els  = document.querySelectorAll(item.el),
					displayNum = i+1;

					//Loop through all items with annotations
					for (k = 0; k < els.length; k++) {
						els[k].classList.add("has-annotation");

						var numberDiv = document.createElement("a");
						numberDiv.href = "#annotation-" + displayNum;
						numberDiv.classList.add("annotation-tip");

						els[k].appendChild(numberDiv);
						numberDiv.innerHTML = displayNum;
					}
				}

				// count elements so it can be used when displaying the results in the viewer
				var count = 0;

				// iterate over the comments in annotations.js
				for(i = 0; i < comments.comments.length; i++) {

					var item = comments.comments[i];
					var els  = document.querySelectorAll(item.el);

					// if an element is found in the given pattern add it to the overall object so it can be passed when the overlay is turned on
					if (els.length > 0) {
						annotationsPattern.commentsGathered.comments[count] = { "el": item.el, "title": item.title, "comment": item.comment };
						count++;
					}

				}

				annotationsPattern.commentsGathered.count = count;

				// send the list of annotations for the page back to the parent
				var targetOrigin = (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host;
				parent.postMessage(annotationsPattern.commentsGathered,targetOrigin);

			} else if (annotationsPattern.commentsEmbeddedActive && !annotationsPattern.commentsEmbedded) {

				// if comment embedding is turned on and comments haven't been embedded yet do it
				for (i = 0; i < comments.comments.length; i++)  {
					item = comments.comments[i];
					els  = document.querySelectorAll(item.el);
					if (els.length > 0) {
						annotationsPattern.embedComments(els[0],item.title,item.comment); //Embed the comment
					}
					annotationsPattern.commentsEmbedded = true;
				}

			} else if (annotationsPattern.commentsEmbeddedActive && annotationsPattern.commentsEmbedded) {

				// if comment embedding is turned on and comments have been embedded simply display them
				els = document.getElementsByClassName("sg-annotations");
				for (i = 0; i < els.length; ++i) {
					els[i].style.display = "block";
				}

			}

		}

	}

};

// add the onclick handlers to the elements that have an annotations
annotationsPattern.gatherComments();
window.addEventListener("message", annotationsPattern.receiveIframeMessage, false);

// before unloading the iframe make sure any active overlay is turned off/closed
window.onbeforeunload = function() {
	var obj = { "commentOverlay": "off" };
	var targetOrigin = (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host;
	parent.postMessage(obj,targetOrigin);
};<|endoftext|>