<commit_before>import { readFileSync } from 'fs'
import { graphqlHTTP } from 'express-graphql'
import { join } from 'path'
import setupBridge from '../../lib/graphql-bookshelf-bridge'
import { presentQuerySet } from '../../lib/graphql-bookshelf-bridge/util'
import {
  acceptGroupRelationshipInvite,
  acceptJoinRequest,
  addModerator,
  addPeopleToProjectRole,
  addSkill,
  addSkillToLearn,
  allowGroupInvites,
  blockUser,
  cancelGroupRelationshipInvite,
  cancelJoinRequest,
  createAffiliation,
  createComment,
  createGroup,
  createInvitation,
  createJoinRequest,
  createMessage,
  createPost,
  createProject,
  createProjectRole,
  createSavedSearch,
  createTopic,
  declineJoinRequest,
  deleteAffiliation,
  deleteComment,
  deleteGroup,
  deleteGroupRelationship,
  deleteGroupTopic,
  deletePost,
  deleteProjectRole,
  deleteSavedSearch,
  expireInvitation,
  findOrCreateLinkPreviewByUrl,
  findOrCreateLocation,
  findOrCreateThread,
  flagInappropriateContent,
  fulfillPost,
  inviteGroupToGroup,
  invitePeopleToEvent,
  joinGroup,
  joinProject,
  leaveGroup,
  leaveProject,
  markActivityRead,
  markAllActivitiesRead,
  pinPost,
  processStripeToken,
  regenerateAccessCode,
  registerDevice,
  registerStripeAccount,
  reinviteAll,
  rejectGroupRelationshipInvite,
  removeMember,
  removeModerator,
  removePost,
  removeSkill,
  removeSkillToLearn,
  resendInvitation,
  respondToEvent,
  subscribe,
  toggleGroupWidgetVisibility,
  unblockUser,
  unfulfillPost,
  unlinkAccount,
  updateComment,
  updateGroup,
  updateGroupHiddenSetting,
  updateGroupTopic,
  updateGroupTopicFollow,
  updateMe,
  updateMembership,
  updatePost,
  updateStripeAccount,
  updateWidgetSettings,
  useInvitation,
  vote
} from './mutations'
import InvitationService from '../services/InvitationService'
import makeModels from './makeModels'
import { makeExecutableSchema } from 'graphql-tools'
import { inspect } from 'util'
import { red } from 'chalk'
import { mapValues, merge, reduce } from 'lodash'

const schemaText = readFileSync(join(__dirname, 'schema.graphql')).toString()

async function createSchema (userId, isAdmin) {
  const models = await makeModels(userId, isAdmin)
  const { resolvers, fetchOne, fetchMany } = setupBridge(models)

  let allResolvers = Object.assign({
    Query: userId ? makeAuthenticatedQueries(userId, fetchOne, fetchMany) : makePublicQueries(userId, fetchOne, fetchMany),
    Mutation: userId ? makeMutations(userId, isAdmin) : {},

    FeedItemContent: {
      __resolveType (data, context, info) {
        if (data instanceof bookshelf.Model) {
          return info.schema.getType('Post')
        }
        throw new Error('Post is the only implemented FeedItemContent type')
      }
    },

    SearchResultContent: {
      __resolveType (data, context, info) {
        return getTypeForInstance(data, models)
      }
    }
  }, resolvers)

  return makeExecutableSchema({
    typeDefs: [schemaText],
    resolvers: allResolvers
  })
}

// Queries that non-logged in users can make
export function makePublicQueries (userId, fetchOne, fetchMany) {
  return {
    // Can only access public communities and posts
    group: async (root, { id, slug }) => fetchOne('Group', slug || id, slug ? 'slug' : 'id', { isPublic: true }),
    groups: (root, args) => fetchMany('Group', Object.assign(args, { isPublic: true })),
    posts: (root, args) => fetchMany('Post', Object.assign(args, { isPublic: true })),
    checkInvitation: (root, { invitationToken, accessCode }) =>
      InvitationService.check(userId, invitationToken, accessCode)
  }
}

// Queries that logged in users can make
export function makeAuthenticatedQueries (userId, fetchOne, fetchMany) {
  return {
    activity: (root, { id }) => fetchOne('Activity', id),
    me: () => fetchOne('Me', userId),
    group: async (root, { id, slug, updateLastViewed }) => {
      // you can specify id or slug, but not both
      const group = await fetchOne('Group', slug || id, slug ? 'slug' : 'id')
      if (updateLastViewed && group) {
        const membership = await GroupMembership.forPair(userId, group).fetch()
        if (membership) {
          await membership.addSetting({ lastReadAt: new Date() }, true)
        }
      }
      return group
    },
    groupExists: (root, { slug }) => {
      if (Group.isSlugValid(slug)) {
        return Group.where(bookshelf.knex.raw('slug = ?', slug))
        .count()
        .then(count => {
          if (count > 0) return {exists: true}
          return {exists: false}
        })
      }
      throw new Error('Slug is invalid')
    },
    joinRequests: (root, args) => fetchMany('JoinRequest', args),
    groups: (root, args) => fetchMany('Group', args),
    notifications: (root, { first, offset, resetCount, order = 'desc' }) => {
      return fetchMany('Notification', { first, offset, order })
      .tap(() => resetCount && User.resetNewNotificationCount(userId))
    },
    person: (root, { id }) => fetchOne('Person', id),
    messageThread: (root, { id }) => fetchOne('MessageThread', id),
    post: (root, { id }) => fetchOne('Post', id),
    posts: (root, args) => fetchMany('Post', args),
    comment: (root, { id }) => fetchOne('Comment', id),
    people: (root, args) => fetchMany('Person', args),
    connections: (root, args) => fetchMany('PersonConnection', args),
    groupTopics: (root, args) => fetchMany('GroupTopic', args),
    topics: (root, args) => fetchMany('Topic', args),
    topic: (root, { id, name }) => // you can specify id or name, but not both
      fetchOne('Topic', name || id, name ? 'name' : 'id'),
    groupTopic: (root, { topicName, groupSlug }) =>
      GroupTag.findByTagAndGroup(topicName, groupSlug),
    search: (root, args) => {
      if (!args.first) args.first = 20
      return Search.fullTextSearch(userId, args)
      .then(({ models, total }) => {
        // FIXME this shouldn't be used directly here -- there should be some
        // way of integrating this into makeModels and using the presentation
        // logic that's already in the fetcher
        return presentQuerySet(models, merge(args, {total}))
      })
    },
    skills: (root, args) => fetchMany('Skill', args),
    checkInvitation: (root, { invitationToken, accessCode }) =>
      InvitationService.check(userId, invitationToken, accessCode),
    savedSearches: (root, args) => fetchMany('SavedSearch', args),
  }
}

export function makeMutations (userId, isAdmin) {
  return {
    acceptGroupRelationshipInvite: (root, { groupRelationshipInviteId }) => acceptGroupRelationshipInvite(userId, groupRelationshipInviteId),

    acceptJoinRequest: (root, { joinRequestId }) => acceptJoinRequest(userId, joinRequestId),

    addModerator: (root, { personId, groupId }) =>
      addModerator(userId, personId, groupId),

    addPeopleToProjectRole: (root, { peopleIds, projectRoleId }) =>
      addPeopleToProjectRole(userId, peopleIds, projectRoleId),

    addSkill: (root, { name }) => addSkill(userId, name),
    addSkillToLearn: (root, { name }) => addSkillToLearn(userId, name),

    allowGroupInvites: (root, { groupId, data }) => allowGroupInvites(groupId, data),

    blockUser: (root, { blockedUserId }) => blockUser(userId, blockedUserId),

    cancelGroupRelationshipInvite: (root, { groupRelationshipInviteId }) => cancelGroupRelationshipInvite(userId, groupRelationshipInviteId),

    cancelJoinRequest: (root, { joinRequestId }) => cancelJoinRequest(userId, joinRequestId),

    createAffiliation: (root, { data }) => createAffiliation(userId, data),

    createComment: (root, { data }) => createComment(userId, data),

    createGroup: (root, { data }) => createGroup(userId, data),

    createInvitation: (root, {groupId, data}) =>
      createInvitation(userId, groupId, data),

    createJoinRequest: (root, {groupId, questionAnswers}) => createJoinRequest(userId, groupId, questionAnswers),

    createMessage: (root, { data }) => createMessage(userId, data),

    createPost: (root, { data }) => createPost(userId, data),

    createProject: (root, { data }) => createProject(userId, data),

    createProjectRole: (root, { projectId, roleName }) => createProjectRole(userId, projectId, roleName),

    createSavedSearch: (root, { data }) => createSavedSearch(data),

    joinGroup: (root, {groupId}) => joinGroup(groupId, userId),

    joinProject: (root, { id }) => joinProject(id, userId),

    createTopic: (root, { topicName, groupId, isDefault, isSubscribing }) => createTopic(userId, topicName, groupId, isDefault, isSubscribing),

    declineJoinRequest: (root, { joinRequestId }) => declineJoinRequest(userId, joinRequestId),

    deleteAffiliation: (root, { id }) => deleteAffiliation(userId, id),

    deleteComment: (root, { id }) => deleteComment(userId, id),

    deleteGroup: (root, { id }) => deleteGroup(userId, id),

    deleteGroupRelationship: (root, { parentId, childId }) => deleteGroupRelationship(userId, parentId, childId),

    deleteGroupTopic: (root, { id }) => deleteGroupTopic(userId, id),

    deletePost: (root, { id }) => deletePost(userId, id),

    deleteProjectRole: (root, { id }) => deleteProjectRole(userId, id),

    deleteSavedSearch: (root, { id }) => deleteSavedSearch(id),

    expireInvitation: (root, {invitationId}) =>
      expireInvitation(userId, invitationId),

    findOrCreateThread: (root, { data }) => findOrCreateThread(userId, data),

    findOrCreateLinkPreviewByUrl: (root, { data }) =>
      findOrCreateLinkPreviewByUrl(data),

    findOrCreateLocation: (root, { data }) => findOrCreateLocation(data),

    flagInappropriateContent: (root, { data }) =>
      flagInappropriateContent(userId, data),

    fulfillPost: (root, { postId }) => fulfillPost(userId, postId),

    inviteGroupToJoinParent: (root, { parentId, childId }) =>
      inviteGroupToGroup(userId, parentId, childId, GroupRelationshipInvite.TYPE.ParentToChild),

    invitePeopleToEvent: (root, {eventId, inviteeIds}) =>
      invitePeopleToEvent(userId, eventId, inviteeIds),

    leaveGroup: (root, { id }) => leaveGroup(userId, id),

    leaveProject: (root, { id }) => leaveProject(id, userId),

    markActivityRead: (root, { id }) => markActivityRead(userId, id),

    markAllActivitiesRead: (root) => markAllActivitiesRead(userId),

    pinPost: (root, { postId, groupId }) =>
      pinPost(userId, postId, groupId),

    processStripeToken: (root, { postId, token, amount }) =>
      processStripeToken(userId, postId, token, amount),

    regenerateAccessCode: (root, { groupId }) =>
      regenerateAccessCode(userId, groupId),

    registerDevice: (root, { playerId, platform, version }) =>
      registerDevice(userId, { playerId, platform, version }),

    registerStripeAccount: (root, { authorizationCode }) =>
      registerStripeAccount(userId, authorizationCode),

    reinviteAll: (root, {groupId}) => reinviteAll(userId, groupId),

    rejectGroupRelationshipInvite: (root, { groupRelationshipInviteId }) => rejectGroupRelationshipInvite(userId, groupRelationshipInviteId),

    removeMember: (root, { personId, groupId }) =>
      removeMember(userId, personId, groupId),

    removeModerator: (root, { personId, groupId, isRemoveFromGroup }) =>
      removeModerator(userId, personId, groupId, isRemoveFromGroup),

    removePost: (root, { postId, groupId, slug }) =>
      removePost(userId, postId, groupId || slug),

    removeSkill: (root, { id, name }) => removeSkill(userId, id || name),
    removeSkillToLearn: (root, { id, name }) => removeSkillToLearn(userId, id || name),

    requestToAddGroupToParent: (root, { parentId, childId }) =>
      inviteGroupToGroup(userId, childId, parentId, GroupRelationshipInvite.TYPE.ChildToParent),

    resendInvitation: (root, {invitationId}) =>
      resendInvitation(userId, invitationId),

    respondToEvent: (root, {id, response}) =>
      respondToEvent(userId, id, response),

    subscribe: (root, { groupId, topicId, isSubscribing }) =>
      subscribe(userId, topicId, groupId, isSubscribing),

    toggleGroupWidgetVisibility: (root, data) => {
      const { id, isVisible } = data
      toggleGroupWidgetVisibility(id, isVisible)
    },

    unblockUser: (root, { blockedUserId }) => unblockUser(userId, blockedUserId),

    unfulfillPost: (root, { postId }) => unfulfillPost(userId, postId),

    unlinkAccount: (root, { provider }) =>
      unlinkAccount(userId, provider),

    updateGroupSettings: (root, { id, changes }) =>
      updateGroup(userId, id, changes),

    updateGroupHiddenSetting: (root, { id, hidden }) =>
      updateGroupHiddenSetting({ userId, isAdmin }, id, hidden),

    updateGroupTopic: (root, { id, data }) => updateGroupTopic(id, data),

    // TODO: need this and the one above?
    updateGroupTopicFollow: (root, args) => updateGroupTopic(userId, args),

    updateMe: (root, { changes }) => updateMe(userId, changes),

    updateMembership: (root, args) => updateMembership(userId, args),

    updatePost: (root, args) => updatePost(userId, args),
    updateComment: (root, args) => updateComment(userId, args),

    updateStripeAccount: (root, { accountId }) => updateStripeAccount(userId, accountId),

    updateWidgetSettings: (root, { id, data }) => updateWidgetSettings(id, data),

    useInvitation: (root, { invitationToken, accessCode }) =>
      useInvitation(userId, invitationToken, accessCode),

    vote: (root, { postId, isUpvote }) => vote(userId, postId, isUpvote)
  }
}

export const createRequestHandler = () =>
  graphqlHTTP(async (req, res) => {
    if (process.env.DEBUG_GRAPHQL) {
      sails.log.info('\n' +
        red('graphql query start') + '\n' +
        req.body.query + '\n' +
        red('graphql query end')
      )
      sails.log.info(inspect(req.body.variables))
    }

    // TODO: since this function can return a promise, we could run through some
    // policies based on the current user here and assign them to context, so
    // that the resolvers can use them to deny or restrict access...
    //
    // ideally we would be able to associate paths with policies, analyze the
    // query to find the policies which should be tested, and run them to allow
    // or deny access to those paths

    await User.query().where({ id: req.session.userId }).update({ last_active_at: new Date() })

    const schema = await createSchema(req.session.userId, Admin.isSignedIn(req))
    return {
      schema,
      graphiql: true,
      customFormatErrorFn: process.env.NODE_ENV === 'development' ? logError : null
    }
  })

var modelToTypeMap

function getTypeForInstance (instance, models) {
  if (!modelToTypeMap) {
    modelToTypeMap = reduce(models, (m, v, k) => {
      const tableName = v.model.forge().tableName
      if (!m[tableName] || v.isDefaultTypeForTable) {
        m[tableName] = k
      }
      return m
    }, {})
  }

  return modelToTypeMap[instance.tableName]
}

function logError (error) {
  console.error(error.stack)

  return {
    message: error.message,
    locations: error.locations,
    stack: error.stack,
    path: error.path
  }
}
<commit_msg>Turn off last_active update for now as its killing all queries for me<commit_after>import { readFileSync } from 'fs'
import { graphqlHTTP } from 'express-graphql'
import { join } from 'path'
import setupBridge from '../../lib/graphql-bookshelf-bridge'
import { presentQuerySet } from '../../lib/graphql-bookshelf-bridge/util'
import {
  acceptGroupRelationshipInvite,
  acceptJoinRequest,
  addModerator,
  addPeopleToProjectRole,
  addSkill,
  addSkillToLearn,
  allowGroupInvites,
  blockUser,
  cancelGroupRelationshipInvite,
  cancelJoinRequest,
  createAffiliation,
  createComment,
  createGroup,
  createInvitation,
  createJoinRequest,
  createMessage,
  createPost,
  createProject,
  createProjectRole,
  createSavedSearch,
  createTopic,
  declineJoinRequest,
  deleteAffiliation,
  deleteComment,
  deleteGroup,
  deleteGroupRelationship,
  deleteGroupTopic,
  deletePost,
  deleteProjectRole,
  deleteSavedSearch,
  expireInvitation,
  findOrCreateLinkPreviewByUrl,
  findOrCreateLocation,
  findOrCreateThread,
  flagInappropriateContent,
  fulfillPost,
  inviteGroupToGroup,
  invitePeopleToEvent,
  joinGroup,
  joinProject,
  leaveGroup,
  leaveProject,
  markActivityRead,
  markAllActivitiesRead,
  pinPost,
  processStripeToken,
  regenerateAccessCode,
  registerDevice,
  registerStripeAccount,
  reinviteAll,
  rejectGroupRelationshipInvite,
  removeMember,
  removeModerator,
  removePost,
  removeSkill,
  removeSkillToLearn,
  resendInvitation,
  respondToEvent,
  subscribe,
  toggleGroupWidgetVisibility,
  unblockUser,
  unfulfillPost,
  unlinkAccount,
  updateComment,
  updateGroup,
  updateGroupHiddenSetting,
  updateGroupTopic,
  updateGroupTopicFollow,
  updateMe,
  updateMembership,
  updatePost,
  updateStripeAccount,
  updateWidgetSettings,
  useInvitation,
  vote
} from './mutations'
import InvitationService from '../services/InvitationService'
import makeModels from './makeModels'
import { makeExecutableSchema } from 'graphql-tools'
import { inspect } from 'util'
import { red } from 'chalk'
import { mapValues, merge, reduce } from 'lodash'

const schemaText = readFileSync(join(__dirname, 'schema.graphql')).toString()

async function createSchema (userId, isAdmin) {
  const models = await makeModels(userId, isAdmin)
  const { resolvers, fetchOne, fetchMany } = setupBridge(models)

  let allResolvers = Object.assign({
    Query: userId ? makeAuthenticatedQueries(userId, fetchOne, fetchMany) : makePublicQueries(userId, fetchOne, fetchMany),
    Mutation: userId ? makeMutations(userId, isAdmin) : {},

    FeedItemContent: {
      __resolveType (data, context, info) {
        if (data instanceof bookshelf.Model) {
          return info.schema.getType('Post')
        }
        throw new Error('Post is the only implemented FeedItemContent type')
      }
    },

    SearchResultContent: {
      __resolveType (data, context, info) {
        return getTypeForInstance(data, models)
      }
    }
  }, resolvers)

  return makeExecutableSchema({
    typeDefs: [schemaText],
    resolvers: allResolvers
  })
}

// Queries that non-logged in users can make
export function makePublicQueries (userId, fetchOne, fetchMany) {
  return {
    // Can only access public communities and posts
    group: async (root, { id, slug }) => fetchOne('Group', slug || id, slug ? 'slug' : 'id', { isPublic: true }),
    groups: (root, args) => fetchMany('Group', Object.assign(args, { isPublic: true })),
    posts: (root, args) => fetchMany('Post', Object.assign(args, { isPublic: true })),
    checkInvitation: (root, { invitationToken, accessCode }) =>
      InvitationService.check(userId, invitationToken, accessCode)
  }
}

// Queries that logged in users can make
export function makeAuthenticatedQueries (userId, fetchOne, fetchMany) {
  return {
    activity: (root, { id }) => fetchOne('Activity', id),
    me: () => fetchOne('Me', userId),
    group: async (root, { id, slug, updateLastViewed }) => {
      // you can specify id or slug, but not both
      const group = await fetchOne('Group', slug || id, slug ? 'slug' : 'id')
      if (updateLastViewed && group) {
        const membership = await GroupMembership.forPair(userId, group).fetch()
        if (membership) {
          await membership.addSetting({ lastReadAt: new Date() }, true)
        }
      }
      return group
    },
    groupExists: (root, { slug }) => {
      if (Group.isSlugValid(slug)) {
        return Group.where(bookshelf.knex.raw('slug = ?', slug))
        .count()
        .then(count => {
          if (count > 0) return {exists: true}
          return {exists: false}
        })
      }
      throw new Error('Slug is invalid')
    },
    joinRequests: (root, args) => fetchMany('JoinRequest', args),
    groups: (root, args) => fetchMany('Group', args),
    notifications: (root, { first, offset, resetCount, order = 'desc' }) => {
      return fetchMany('Notification', { first, offset, order })
      .tap(() => resetCount && User.resetNewNotificationCount(userId))
    },
    person: (root, { id }) => fetchOne('Person', id),
    messageThread: (root, { id }) => fetchOne('MessageThread', id),
    post: (root, { id }) => fetchOne('Post', id),
    posts: (root, args) => fetchMany('Post', args),
    comment: (root, { id }) => fetchOne('Comment', id),
    people: (root, args) => fetchMany('Person', args),
    connections: (root, args) => fetchMany('PersonConnection', args),
    groupTopics: (root, args) => fetchMany('GroupTopic', args),
    topics: (root, args) => fetchMany('Topic', args),
    topic: (root, { id, name }) => // you can specify id or name, but not both
      fetchOne('Topic', name || id, name ? 'name' : 'id'),
    groupTopic: (root, { topicName, groupSlug }) =>
      GroupTag.findByTagAndGroup(topicName, groupSlug),
    search: (root, args) => {
      if (!args.first) args.first = 20
      return Search.fullTextSearch(userId, args)
      .then(({ models, total }) => {
        // FIXME this shouldn't be used directly here -- there should be some
        // way of integrating this into makeModels and using the presentation
        // logic that's already in the fetcher
        return presentQuerySet(models, merge(args, {total}))
      })
    },
    skills: (root, args) => fetchMany('Skill', args),
    checkInvitation: (root, { invitationToken, accessCode }) =>
      InvitationService.check(userId, invitationToken, accessCode),
    savedSearches: (root, args) => fetchMany('SavedSearch', args),
  }
}

export function makeMutations (userId, isAdmin) {
  return {
    acceptGroupRelationshipInvite: (root, { groupRelationshipInviteId }) => acceptGroupRelationshipInvite(userId, groupRelationshipInviteId),

    acceptJoinRequest: (root, { joinRequestId }) => acceptJoinRequest(userId, joinRequestId),

    addModerator: (root, { personId, groupId }) =>
      addModerator(userId, personId, groupId),

    addPeopleToProjectRole: (root, { peopleIds, projectRoleId }) =>
      addPeopleToProjectRole(userId, peopleIds, projectRoleId),

    addSkill: (root, { name }) => addSkill(userId, name),
    addSkillToLearn: (root, { name }) => addSkillToLearn(userId, name),

    allowGroupInvites: (root, { groupId, data }) => allowGroupInvites(groupId, data),

    blockUser: (root, { blockedUserId }) => blockUser(userId, blockedUserId),

    cancelGroupRelationshipInvite: (root, { groupRelationshipInviteId }) => cancelGroupRelationshipInvite(userId, groupRelationshipInviteId),

    cancelJoinRequest: (root, { joinRequestId }) => cancelJoinRequest(userId, joinRequestId),

    createAffiliation: (root, { data }) => createAffiliation(userId, data),

    createComment: (root, { data }) => createComment(userId, data),

    createGroup: (root, { data }) => createGroup(userId, data),

    createInvitation: (root, {groupId, data}) =>
      createInvitation(userId, groupId, data),

    createJoinRequest: (root, {groupId, questionAnswers}) => createJoinRequest(userId, groupId, questionAnswers),

    createMessage: (root, { data }) => createMessage(userId, data),

    createPost: (root, { data }) => createPost(userId, data),

    createProject: (root, { data }) => createProject(userId, data),

    createProjectRole: (root, { projectId, roleName }) => createProjectRole(userId, projectId, roleName),

    createSavedSearch: (root, { data }) => createSavedSearch(data),

    joinGroup: (root, {groupId}) => joinGroup(groupId, userId),

    joinProject: (root, { id }) => joinProject(id, userId),

    createTopic: (root, { topicName, groupId, isDefault, isSubscribing }) => createTopic(userId, topicName, groupId, isDefault, isSubscribing),

    declineJoinRequest: (root, { joinRequestId }) => declineJoinRequest(userId, joinRequestId),

    deleteAffiliation: (root, { id }) => deleteAffiliation(userId, id),

    deleteComment: (root, { id }) => deleteComment(userId, id),

    deleteGroup: (root, { id }) => deleteGroup(userId, id),

    deleteGroupRelationship: (root, { parentId, childId }) => deleteGroupRelationship(userId, parentId, childId),

    deleteGroupTopic: (root, { id }) => deleteGroupTopic(userId, id),

    deletePost: (root, { id }) => deletePost(userId, id),

    deleteProjectRole: (root, { id }) => deleteProjectRole(userId, id),

    deleteSavedSearch: (root, { id }) => deleteSavedSearch(id),

    expireInvitation: (root, {invitationId}) =>
      expireInvitation(userId, invitationId),

    findOrCreateThread: (root, { data }) => findOrCreateThread(userId, data),

    findOrCreateLinkPreviewByUrl: (root, { data }) =>
      findOrCreateLinkPreviewByUrl(data),

    findOrCreateLocation: (root, { data }) => findOrCreateLocation(data),

    flagInappropriateContent: (root, { data }) =>
      flagInappropriateContent(userId, data),

    fulfillPost: (root, { postId }) => fulfillPost(userId, postId),

    inviteGroupToJoinParent: (root, { parentId, childId }) =>
      inviteGroupToGroup(userId, parentId, childId, GroupRelationshipInvite.TYPE.ParentToChild),

    invitePeopleToEvent: (root, {eventId, inviteeIds}) =>
      invitePeopleToEvent(userId, eventId, inviteeIds),

    leaveGroup: (root, { id }) => leaveGroup(userId, id),

    leaveProject: (root, { id }) => leaveProject(id, userId),

    markActivityRead: (root, { id }) => markActivityRead(userId, id),

    markAllActivitiesRead: (root) => markAllActivitiesRead(userId),

    pinPost: (root, { postId, groupId }) =>
      pinPost(userId, postId, groupId),

    processStripeToken: (root, { postId, token, amount }) =>
      processStripeToken(userId, postId, token, amount),

    regenerateAccessCode: (root, { groupId }) =>
      regenerateAccessCode(userId, groupId),

    registerDevice: (root, { playerId, platform, version }) =>
      registerDevice(userId, { playerId, platform, version }),

    registerStripeAccount: (root, { authorizationCode }) =>
      registerStripeAccount(userId, authorizationCode),

    reinviteAll: (root, {groupId}) => reinviteAll(userId, groupId),

    rejectGroupRelationshipInvite: (root, { groupRelationshipInviteId }) => rejectGroupRelationshipInvite(userId, groupRelationshipInviteId),

    removeMember: (root, { personId, groupId }) =>
      removeMember(userId, personId, groupId),

    removeModerator: (root, { personId, groupId, isRemoveFromGroup }) =>
      removeModerator(userId, personId, groupId, isRemoveFromGroup),

    removePost: (root, { postId, groupId, slug }) =>
      removePost(userId, postId, groupId || slug),

    removeSkill: (root, { id, name }) => removeSkill(userId, id || name),
    removeSkillToLearn: (root, { id, name }) => removeSkillToLearn(userId, id || name),

    requestToAddGroupToParent: (root, { parentId, childId }) =>
      inviteGroupToGroup(userId, childId, parentId, GroupRelationshipInvite.TYPE.ChildToParent),

    resendInvitation: (root, {invitationId}) =>
      resendInvitation(userId, invitationId),

    respondToEvent: (root, {id, response}) =>
      respondToEvent(userId, id, response),

    subscribe: (root, { groupId, topicId, isSubscribing }) =>
      subscribe(userId, topicId, groupId, isSubscribing),

    toggleGroupWidgetVisibility: (root, data) => {
      const { id, isVisible } = data
      toggleGroupWidgetVisibility(id, isVisible)
    },

    unblockUser: (root, { blockedUserId }) => unblockUser(userId, blockedUserId),

    unfulfillPost: (root, { postId }) => unfulfillPost(userId, postId),

    unlinkAccount: (root, { provider }) =>
      unlinkAccount(userId, provider),

    updateGroupSettings: (root, { id, changes }) =>
      updateGroup(userId, id, changes),

    updateGroupHiddenSetting: (root, { id, hidden }) =>
      updateGroupHiddenSetting({ userId, isAdmin }, id, hidden),

    updateGroupTopic: (root, { id, data }) => updateGroupTopic(id, data),

    // TODO: need this and the one above?
    updateGroupTopicFollow: (root, args) => updateGroupTopic(userId, args),

    updateMe: (root, { changes }) => updateMe(userId, changes),

    updateMembership: (root, args) => updateMembership(userId, args),

    updatePost: (root, args) => updatePost(userId, args),
    updateComment: (root, args) => updateComment(userId, args),

    updateStripeAccount: (root, { accountId }) => updateStripeAccount(userId, accountId),

    updateWidgetSettings: (root, { id, data }) => updateWidgetSettings(id, data),

    useInvitation: (root, { invitationToken, accessCode }) =>
      useInvitation(userId, invitationToken, accessCode),

    vote: (root, { postId, isUpvote }) => vote(userId, postId, isUpvote)
  }
}

export const createRequestHandler = () =>
  graphqlHTTP(async (req, res) => {
    if (process.env.DEBUG_GRAPHQL) {
      sails.log.info('\n' +
        red('graphql query start') + '\n' +
        req.body.query + '\n' +
        red('graphql query end')
      )
      sails.log.info(inspect(req.body.variables))
    }

    // TODO: since this function can return a promise, we could run through some
    // policies based on the current user here and assign them to context, so
    // that the resolvers can use them to deny or restrict access...
    //
    // ideally we would be able to associate paths with policies, analyze the
    // query to find the policies which should be tested, and run them to allow
    // or deny access to those paths

    // await User.query().where({ id: req.session.userId }).update({ last_active_at: new Date() })

    const schema = await createSchema(req.session.userId, Admin.isSignedIn(req))
    return {
      schema,
      graphiql: true,
      customFormatErrorFn: process.env.NODE_ENV === 'development' ? logError : null
    }
  })

var modelToTypeMap

function getTypeForInstance (instance, models) {
  if (!modelToTypeMap) {
    modelToTypeMap = reduce(models, (m, v, k) => {
      const tableName = v.model.forge().tableName
      if (!m[tableName] || v.isDefaultTypeForTable) {
        m[tableName] = k
      }
      return m
    }, {})
  }

  return modelToTypeMap[instance.tableName]
}

function logError (error) {
  console.error(error.stack)

  return {
    message: error.message,
    locations: error.locations,
    stack: error.stack,
    path: error.path
  }
}
<|endoftext|>


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

    var $ = require('jquery');
    var console = require('wf-js-common/console');
    var DeviceInfo = require('wf-js-common/DeviceInfo');
    var DOMUtil = require('wf-js-common/DOMUtil');
    var ItemSizeCollection = require('wf-js-uicomponents/layouts/ItemSizeCollection');
    var KeyNavigator = require('wf-js-uicomponents/scroll_list/KeyNavigator');
    var ScrollBar = require('wf-js-uicomponents/scroll_bar/ScrollBar');
    var ScrollList = require('wf-js-uicomponents/scroll_list/ScrollList');
    var Url = require('wf-js-common/Url');
    var Utils = require('wf-js-common/Utils');

    //---------------------------------------------------------
    // Initialize ViewerComponent
    //---------------------------------------------------------

    function generateItemSizes(length) {
        // NOTE: Interested in retina canvas performance?
        // See: http://www.scirra.com/forum/retina-ios-performance-problem-fix-please-test_topic58742.html
        var tallItem = { height: 1022, width: 766 };
        var wideItem = { height: 766, width: 1022 };
        var result = [];
        for (var i = 0; i < length; i++) {
            var item = (i % 2 === 0) ? tallItem : wideItem;
            result.push(item);
        }

        return result;
    }

    function createPage(container, pageIndex, scale, width, height) {
        var pixelRatio = DeviceInfo.devicePixelRatio;

        // Create the canvas element.
        var canvas = document.createElement('canvas');
        canvas.style.position = 'absolute';

        // NOTE: Account for retina displays! (Incl setting ctx.scale below.)
        canvas.height = height * pixelRatio;
        canvas.width = width * pixelRatio;
        canvas.style.height = height + 'px';
        canvas.style.width = width + 'px';

        // Initialize the drawing context.
        var ctx = canvas.getContext('2d');
        ctx.scale(pixelRatio, pixelRatio);
        ctx.fillStyle = '#fff';
        ctx.fillRect(0, 0, width, height);

        // Render the page label.
        ctx.fillStyle = '#333';
        ctx.font = 'bold 20px sans-serif';
        ctx.textBaseline = 'top';
        ctx.textAlign = 'center';
        ctx.fillText('Page ' + (pageIndex + 1), width / 2, 5);

        // Render the canvas checkerboard.
        var checkerboardMargin = 30;
        var checkerboardSquareCount = 10;
        var exactCheckerboardHeight = height - checkerboardMargin * 2;
        var exactCheckerboardWidth = width - checkerboardMargin * 2;
        var excessCheckerboardHeight = exactCheckerboardHeight % checkerboardSquareCount;
        var excessCheckerboardWidth = exactCheckerboardWidth % checkerboardSquareCount;
        var squareHeight = (exactCheckerboardHeight - excessCheckerboardHeight) / checkerboardSquareCount;
        var squareWidth = (exactCheckerboardWidth - excessCheckerboardWidth) / checkerboardSquareCount;
        var patternLeft = checkerboardMargin + (excessCheckerboardWidth / 2);
        var patternTop = checkerboardMargin + (excessCheckerboardHeight / 2);
        var patternHeight = squareHeight * checkerboardSquareCount;
        var patternWidth = squareWidth * checkerboardSquareCount;

        var patternCanvas = document.createElement('canvas');
        patternCanvas.height = squareHeight * 2;
        patternCanvas.width = squareWidth * 2;

        // Draw if we have non-zero dimensions to draw.
        if (squareWidth && squareHeight) {
            var patternCtx = patternCanvas.getContext('2d');
            patternCtx.fillStyle = (pageIndex % 2 === 0) ? '#69c' : '#c96';
            patternCtx.fillRect(0, 0, squareWidth, squareHeight);
            patternCtx.fillRect(squareWidth, squareHeight, squareWidth, squareHeight);

            var pattern = ctx.createPattern(patternCanvas, 'repeat');
            ctx.fillStyle = pattern;
            ctx.translate(patternLeft, patternTop);
            ctx.fillRect(0, 0, patternWidth, patternHeight);
        }

        // Append to the container.
        container.appendChild(canvas);
    }

    function updateZoomPercentage() {
        if (!scrollList) {
            return;
        }
        $('#zoomTo').val(Math.floor(scrollList.getScale() * 100));
    }

    var url = new Url(window.location);
    var urlParams = url.getParams();
    var scrollMode = urlParams.scroll || (DeviceInfo.desktop ? 'flow' : 'peek');
    var fitMode = urlParams.fit || 'auto';
    var horizontalAlign = urlParams.halign || 'center';
    var totalPages = urlParams.totalPages !== undefined ? urlParams.totalPages : 100;
    var minNumberOfVirtualItems = scrollMode === 'flow' ? (DeviceInfo.desktop ? 15 : 9) : (DeviceInfo.desktop ? 5 : 3);
    var touchScrollingEnabled = Utils.valueOr(urlParams.touchScrollingEnabled, 'true') === 'true';
    var verticalAlign = urlParams.valign || 'auto';
    var zoomMode = urlParams.scroll && urlParams.scroll !== 'flow';

    var itemSizeCollection = new ItemSizeCollection({
        maxWidth: 1022,
        maxHeight: 1022,
        items: generateItemSizes(totalPages)
    });

    var scrollList = window.scrollList = new ScrollList($('#document')[0], itemSizeCollection, {
        fit: fitMode,
        gap: 2,
        horizontalAlign: horizontalAlign,
        minNumberOfVirtualItems: minNumberOfVirtualItems,
        mode: scrollMode,
        padding: 1,
        persistZoom: zoomMode,
        scaleLimits: { minimum: 0.25, maximum: 3 },
        touchScrollingEnabled: touchScrollingEnabled,
        verticalAlign: verticalAlign
    });

    // Instantiate a KeyNavigator
    window._keyNavigator = new KeyNavigator(scrollList);

    // If on desktop, instantiate a scrollBar
    if (DeviceInfo.desktop) {
        var parent = document.getElementById('scrollbar-parent');

        var vertScrollBarOptions = {
            scrollerId: 'scrollbar-vert',
            scrollTrackId: 'scrollbar-vert-container',
            orientation: 'vertical',
            trackMargin: 20,
        };
        window.vertScrollBar = new ScrollBar(scrollList, parent, vertScrollBarOptions);

        var horizScrollBarOptions = {
            scrollerId: 'scrollbar-horiz',
            scrollTrackId: 'scrollbar-horiz-container',
            orientation: 'horizontal',
            trackMargin: 20,
        };
        window.horizScrollBar = new ScrollBar(scrollList, parent, horizScrollBarOptions);
    }

    scrollList.onContentRequested(function(sender, args) {
        var itemIndex = args.itemIndex;
        console.debug('content requested for', itemIndex);
        createPage(args.placeholder.contentContainer, itemIndex,
            args.scaleToFit, args.width, args.height);
    });

    scrollList.onContentRemoved(function(sender, args) {
        console.debug('content removed for', args.itemIndex);
    });

    scrollList.onCurrentItemChanged(function(sender, args) {
        console.debug('current item changed to', args.itemIndex);
        $('#page').val(args.itemIndex + 1);
        updateZoomPercentage();
    });

    scrollList.onInteraction(function(sender, args) {
        console.log(args.event.type, args.event.simulated, args);
    });

    scrollList.onInteractionStarted(function(/*sender*/) {
        console.log('interaction started');
    });

    scrollList.onInteractionFinished(function(/*sender*/) {
        console.log('interaction finished');
    });

    scrollList.onPlaceholderRendered(function(sender, args) {
        console.debug('placeholder rendered for', args.itemIndex);
        // BEWARE: Doing big stuff here can interrupt swipe animations!
        args.placeholder.contentContainer.style.backgroundColor = '#fff';
    });

    scrollList.onScaleChanged(function(sender, args) {
        console.debug('scale changed to', args.scale);
        updateZoomPercentage();
    });

    scrollList.onScrollPositionChanged(function(sender, args) {
        console.log('scroll position changed to', args.x, args.y);
    });

    scrollList.onItemsInserted(function(sender, args) {
        totalPages += args.count;
        $('#totalPages').val(totalPages);
    });

    scrollList.render();

    //---------------------------------------------------------
    // UI control wiring
    //---------------------------------------------------------

    $(function() {

        $(window).on('resize', function() { console.debug('resize'); });
        $(window).on('orientationchange', function() { console.debug('orientationchange'); });
        $(window).on('scroll', function() { console.debug('scroll'); });

        DOMUtil.dismissIOS7VirtualKeyboardOnOrientationChange();
        DOMUtil.preventIOS7WindowScroll();

        $('#scrollMode').val(scrollMode).change(function() {
            window.location = url.addParam('scroll', this.value).toString();
        });

        $('#fitMode').val(fitMode).change(function() {
            window.location = url.addParam('fit', this.value).toString();
        });

        $('#horizAlign').val(horizontalAlign).change(function() {
            window.location = url.addParam('halign', this.value).toString();
        });

        $('#verticalAlign').val(verticalAlign).change(function() {
            window.location = url.addParam('valign', this.value).toString();
        });

        $('#zoomToScale').submit(function() {
            var scale = parseInt(this.elements.zoomTo.value, 10) / 100;
            scrollList.zoomTo({ scale: scale });
            $(':focus').blur();
            return false;
        });

        $('#toggle-disabled').click(function() {
            if ($(this).is(':checked')) {
                scrollList.disable();
            }
            else {
                scrollList.enable();
            }
        });

        $('#jumpToPage').submit(function() {
            scrollList.scrollToItem({
                index: this.elements.page.value - 1,
                offset: { y: 0 }
            });
            $(':focus').blur();
            return false;
        });

        $('#totalPages').val(totalPages).change(function() {
            window.location = url.addParam('totalPages', this.value).toString();
        });

        // Hide iOS browser chrome
        $('body').scrollTop(0);
    });
});
<commit_msg>HY-1872: Make Zoom Persistence Configurable in ScrollList Demo<commit_after>define(function(require) {
    'use strict';

    var $ = require('jquery');
    var console = require('wf-js-common/console');
    var DeviceInfo = require('wf-js-common/DeviceInfo');
    var DOMUtil = require('wf-js-common/DOMUtil');
    var ItemSizeCollection = require('wf-js-uicomponents/layouts/ItemSizeCollection');
    var KeyNavigator = require('wf-js-uicomponents/scroll_list/KeyNavigator');
    var ScrollBar = require('wf-js-uicomponents/scroll_bar/ScrollBar');
    var ScrollList = require('wf-js-uicomponents/scroll_list/ScrollList');
    var Url = require('wf-js-common/Url');
    var Utils = require('wf-js-common/Utils');

    //---------------------------------------------------------
    // Initialize ViewerComponent
    //---------------------------------------------------------

    function generateItemSizes(length) {
        // NOTE: Interested in retina canvas performance?
        // See: http://www.scirra.com/forum/retina-ios-performance-problem-fix-please-test_topic58742.html
        var tallItem = { height: 1022, width: 766 };
        var wideItem = { height: 766, width: 1022 };
        var result = [];
        for (var i = 0; i < length; i++) {
            var item = (i % 2 === 0) ? tallItem : wideItem;
            result.push(item);
        }

        return result;
    }

    function createPage(container, pageIndex, scale, width, height) {
        var pixelRatio = DeviceInfo.devicePixelRatio;

        // Create the canvas element.
        var canvas = document.createElement('canvas');
        canvas.style.position = 'absolute';

        // NOTE: Account for retina displays! (Incl setting ctx.scale below.)
        canvas.height = height * pixelRatio;
        canvas.width = width * pixelRatio;
        canvas.style.height = height + 'px';
        canvas.style.width = width + 'px';

        // Initialize the drawing context.
        var ctx = canvas.getContext('2d');
        ctx.scale(pixelRatio, pixelRatio);
        ctx.fillStyle = '#fff';
        ctx.fillRect(0, 0, width, height);

        // Render the page label.
        ctx.fillStyle = '#333';
        ctx.font = 'bold 20px sans-serif';
        ctx.textBaseline = 'top';
        ctx.textAlign = 'center';
        ctx.fillText('Page ' + (pageIndex + 1), width / 2, 5);

        // Render the canvas checkerboard.
        var checkerboardMargin = 30;
        var checkerboardSquareCount = 10;
        var exactCheckerboardHeight = height - checkerboardMargin * 2;
        var exactCheckerboardWidth = width - checkerboardMargin * 2;
        var excessCheckerboardHeight = exactCheckerboardHeight % checkerboardSquareCount;
        var excessCheckerboardWidth = exactCheckerboardWidth % checkerboardSquareCount;
        var squareHeight = (exactCheckerboardHeight - excessCheckerboardHeight) / checkerboardSquareCount;
        var squareWidth = (exactCheckerboardWidth - excessCheckerboardWidth) / checkerboardSquareCount;
        var patternLeft = checkerboardMargin + (excessCheckerboardWidth / 2);
        var patternTop = checkerboardMargin + (excessCheckerboardHeight / 2);
        var patternHeight = squareHeight * checkerboardSquareCount;
        var patternWidth = squareWidth * checkerboardSquareCount;

        var patternCanvas = document.createElement('canvas');
        patternCanvas.height = squareHeight * 2;
        patternCanvas.width = squareWidth * 2;

        // Draw if we have non-zero dimensions to draw.
        if (squareWidth && squareHeight) {
            var patternCtx = patternCanvas.getContext('2d');
            patternCtx.fillStyle = (pageIndex % 2 === 0) ? '#69c' : '#c96';
            patternCtx.fillRect(0, 0, squareWidth, squareHeight);
            patternCtx.fillRect(squareWidth, squareHeight, squareWidth, squareHeight);

            var pattern = ctx.createPattern(patternCanvas, 'repeat');
            ctx.fillStyle = pattern;
            ctx.translate(patternLeft, patternTop);
            ctx.fillRect(0, 0, patternWidth, patternHeight);
        }

        // Append to the container.
        container.appendChild(canvas);
    }

    function updateZoomPercentage() {
        if (!scrollList) {
            return;
        }
        $('#zoomTo').val(Math.floor(scrollList.getScale() * 100));
    }

    var url = new Url(window.location);
    var urlParams = url.getParams();
    var scrollMode = urlParams.scroll || (DeviceInfo.desktop ? 'flow' : 'peek');
    var fitMode = urlParams.fit || 'auto';
    var horizontalAlign = urlParams.halign || 'center';
    var totalPages = urlParams.totalPages !== undefined ? urlParams.totalPages : 100;
    var minNumberOfVirtualItems = scrollMode === 'flow' ? (DeviceInfo.desktop ? 15 : 9) : (DeviceInfo.desktop ? 5 : 3);
    var touchScrollingEnabled = Utils.valueOr(urlParams.touchScrollingEnabled, 'true') === 'true';
    var verticalAlign = urlParams.valign || 'auto';
    var persistZoom = scrollMode !== 'flow' && (urlParams.persistZoom || false);

    var itemSizeCollection = new ItemSizeCollection({
        maxWidth: 1022,
        maxHeight: 1022,
        items: generateItemSizes(totalPages)
    });

    var scrollList = window.scrollList = new ScrollList($('#document')[0], itemSizeCollection, {
        fit: fitMode,
        gap: 2,
        horizontalAlign: horizontalAlign,
        minNumberOfVirtualItems: minNumberOfVirtualItems,
        mode: scrollMode,
        padding: 1,
        persistZoom: persistZoom,
        scaleLimits: { minimum: 0.25, maximum: 3 },
        touchScrollingEnabled: touchScrollingEnabled,
        verticalAlign: verticalAlign
    });

    // Instantiate a KeyNavigator
    window._keyNavigator = new KeyNavigator(scrollList);

    // If on desktop, instantiate a scrollBar
    if (DeviceInfo.desktop) {
        var parent = document.getElementById('scrollbar-parent');

        var vertScrollBarOptions = {
            scrollerId: 'scrollbar-vert',
            scrollTrackId: 'scrollbar-vert-container',
            orientation: 'vertical',
            trackMargin: 20,
        };
        window.vertScrollBar = new ScrollBar(scrollList, parent, vertScrollBarOptions);

        var horizScrollBarOptions = {
            scrollerId: 'scrollbar-horiz',
            scrollTrackId: 'scrollbar-horiz-container',
            orientation: 'horizontal',
            trackMargin: 20,
        };
        window.horizScrollBar = new ScrollBar(scrollList, parent, horizScrollBarOptions);
    }

    scrollList.onContentRequested(function(sender, args) {
        var itemIndex = args.itemIndex;
        console.debug('content requested for', itemIndex);
        createPage(args.placeholder.contentContainer, itemIndex,
            args.scaleToFit, args.width, args.height);
    });

    scrollList.onContentRemoved(function(sender, args) {
        console.debug('content removed for', args.itemIndex);
    });

    scrollList.onCurrentItemChanged(function(sender, args) {
        console.debug('current item changed to', args.itemIndex);
        $('#page').val(args.itemIndex + 1);
        updateZoomPercentage();
    });

    scrollList.onInteraction(function(sender, args) {
        console.log(args.event.type, args.event.simulated, args);
    });

    scrollList.onInteractionStarted(function(/*sender*/) {
        console.log('interaction started');
    });

    scrollList.onInteractionFinished(function(/*sender*/) {
        console.log('interaction finished');
    });

    scrollList.onPlaceholderRendered(function(sender, args) {
        console.debug('placeholder rendered for', args.itemIndex);
        // BEWARE: Doing big stuff here can interrupt swipe animations!
        args.placeholder.contentContainer.style.backgroundColor = '#fff';
    });

    scrollList.onScaleChanged(function(sender, args) {
        console.debug('scale changed to', args.scale);
        updateZoomPercentage();
    });

    scrollList.onScrollPositionChanged(function(sender, args) {
        console.log('scroll position changed to', args.x, args.y);
    });

    scrollList.onItemsInserted(function(sender, args) {
        totalPages += args.count;
        $('#totalPages').val(totalPages);
    });

    scrollList.render();

    //---------------------------------------------------------
    // UI control wiring
    //---------------------------------------------------------

    $(function() {

        $(window).on('resize', function() { console.debug('resize'); });
        $(window).on('orientationchange', function() { console.debug('orientationchange'); });
        $(window).on('scroll', function() { console.debug('scroll'); });

        DOMUtil.dismissIOS7VirtualKeyboardOnOrientationChange();
        DOMUtil.preventIOS7WindowScroll();

        $('#scrollMode').val(scrollMode).change(function() {
            window.location = url.addParam('scroll', this.value).toString();
        });

        $('#fitMode').val(fitMode).change(function() {
            window.location = url.addParam('fit', this.value).toString();
        });

        $('#horizAlign').val(horizontalAlign).change(function() {
            window.location = url.addParam('halign', this.value).toString();
        });

        $('#verticalAlign').val(verticalAlign).change(function() {
            window.location = url.addParam('valign', this.value).toString();
        });

        $('#zoomToScale').submit(function() {
            var scale = parseInt(this.elements.zoomTo.value, 10) / 100;
            scrollList.zoomTo({ scale: scale });
            $(':focus').blur();
            return false;
        });

        $('#toggle-disabled').click(function() {
            if ($(this).is(':checked')) {
                scrollList.disable();
            }
            else {
                scrollList.enable();
            }
        });

        $('#jumpToPage').submit(function() {
            scrollList.scrollToItem({
                index: this.elements.page.value - 1,
                offset: { y: 0 }
            });
            $(':focus').blur();
            return false;
        });

        $('#totalPages').val(totalPages).change(function() {
            window.location = url.addParam('totalPages', this.value).toString();
        });

        // Hide iOS browser chrome
        $('body').scrollTop(0);
    });
});
<|endoftext|>


<commit_before>var mongoose = require("mongoose");

var userSchema = new mongoose.Schema({
	auth: String,
	id: String,
	username: String,
	displayName: String,
	firstName: String,
	lastName: String,
	email: String,
	link: String,
	picture: String,
	gender: String,
	usertype: String,
	photos: Array
});

var jobsSchema = new mongoose.Schema({
	user: String,
	dateAdded: Date,
	postingAs: String,
	eventName: String,
	dateTime: Date,
	jobDuration: String,
	location: String,
	description: String,
	useOfPhotos: Array,
	dateRequired: String,
	noOfPhotographers: String
});

var User = mongoose.model('user', userSchema, 'user');
var Job = mongoose.model('job', jobsSchema, 'job');

module.exports = {
	User : User,
	Job : Job
};<commit_msg>chat schema<commit_after>var mongoose = require("mongoose");

var userSchema = new mongoose.Schema({
	auth: String,
	id: String,
	username: String,
	displayName: String,
	firstName: String,
	lastName: String,
	email: String,
	link: String,
	picture: String,
	gender: String,
	usertype: String,
	photos: Array,
	workedWith: Array
});

var jobSchema = new mongoose.Schema({
	user: String,
	dateAdded: Date,
	postingAs: String,
	eventName: String,
	dateTime: Date,
	jobDuration: String,
	location: String,
	description: String,
	useOfPhotos: Array,
	dateRequired: String,
	noOfPhotographers: String
});

var chatSchema = new mongoose.Schema({
	firstUser: String,
	secondUser: String,
	history: Array
});

var User = mongoose.model('user', userSchema, 'user');
var Job = mongoose.model('job', jobSchema, 'job');
var Chat = mongoose.model('chat', chatSchema, 'chat');

module.exports = {
	User : User,
	Job : Job,
	Chat : Chat
};<|endoftext|>


<commit_before>const got = require('got');

const time = require('../utils/time.js');

const stops = {};
let last = new Date();

// Update stop/stops in cache.
async function update(id) {

	// Add stop to cache.
	if (id) stops[id] = {
		rank: 5,
		trips: []
	};

	// Decrease ranks and remove stops if too low.
	if (!id) for (const key in stops) if (stops[key].rank > 0) stops[key].rank--; else delete stops[id];

	try {

		const data = (await got(`https://transport.tallinn.ee/siri-stop-departures.php?stopid=${id || Object.keys(stops)}`)).body;

		// Fallback to GTFS trips on error.
		if (id || new Date() - last < 4200) if (data.split('\n').length === 2) {
			if (id) stops[id].trips = []; else for (const key in stops) stops[key].trips = [];
			return;
		}

		// Add stops to cache.
		for (const stop of data.split('\nstop,').slice(1)) stops[stop.split('\n', 1)[0]].trips = stop.split('\n').slice(1, -1).map((trip) => trip.split(',')).map((trip) => ({
			time: Number(trip[3]),
			countdown: Number(trip[3]) - time.getSeconds(),
			name: trip[1],
			destination: trip[4],
			type: trip[0],
			live: trip[2] !== trip[3],
			provider: 'tlt'
		}));

		last = new Date();

	} catch (ex) {
		// Fallback to GTFS data on error.
		if (id || new Date() - last < 4200) if (id) stops[id].trips = []; else for (const key in stops) stops[key].trips = [];
	}

}

// Update stops in cache (2s interval).
setInterval(update, 2000);

// Get trips for stop by stop id.
async function getTrips(id) {

	// Return trips from cache and increase rank if exists.
	if (stops[id]) {
		if (stops[id].rank <= 60) stops[id].rank++;
		return [ ...stops[id].trips ];
	}

	// Force cache update for stop and return trips.
	await update(id);
	return [ ...stops[id].trips ];

}

module.exports = {
	getTrips
};
<commit_msg>Fix TLT time format (web)<commit_after>const got = require('got');

const time = require('../utils/time.js');

const stops = {};
let last = new Date();

// Update stop/stops in cache.
async function update(id) {

	// Add stop to cache.
	if (id) stops[id] = {
		rank: 5,
		trips: []
	};

	// Decrease ranks and remove stops if too low.
	if (!id) for (const key in stops) if (stops[key].rank > 0) stops[key].rank--; else delete stops[id];

	try {

		const data = (await got(`https://transport.tallinn.ee/siri-stop-departures.php?stopid=${id || Object.keys(stops)}`)).body;

		// Fallback to GTFS trips on error.
		if (id || new Date() - last < 4200) if (data.split('\n').length === 2) {
			if (id) stops[id].trips = []; else for (const key in stops) stops[key].trips = [];
			return;
		}

		// Add stops to cache.
		for (const stop of data.split('\nstop,').slice(1)) stops[stop.split('\n', 1)[0]].trips = stop.split('\n').slice(1, -1).map((trip) => trip.split(',')).map((trip) => ({
			time: Number(trip[3]),
			countdown: Number(trip[2]) - time.getSeconds(),
			name: trip[1],
			destination: trip[4],
			type: trip[0],
			live: trip[2] !== trip[3],
			provider: 'tlt'
		}));

		last = new Date();

	} catch (ex) {
		// Fallback to GTFS data on error.
		if (id || new Date() - last < 4200) if (id) stops[id].trips = []; else for (const key in stops) stops[key].trips = [];
	}

}

// Update stops in cache (2s interval).
setInterval(update, 2000);

// Get trips for stop by stop id.
async function getTrips(id) {

	// Return trips from cache and increase rank if exists.
	if (stops[id]) {
		if (stops[id].rank <= 60) stops[id].rank++;
		return [ ...stops[id].trips ];
	}

	// Force cache update for stop and return trips.
	await update(id);
	return [ ...stops[id].trips ];

}

module.exports = {
	getTrips
};
<|endoftext|>


<commit_before>function parseSitemap(sitemap, widgetType) {
  var widgets = sitemap.homepage.widget;
  var result = [];
  for (var i = 0; i < widgets.length; i++) {
    var widget = widgets[i];
    if (!widgetType || widget.type == widgetType) {
      if (!widget.item) {
        console.log("WARN: The widget '" + widget.label + "' does not reference an item.");
        continue;
      }

      result.push(
        {
          type: widget.type,
          name: widget.label,
          link: widget.item.link,
          state: widget.item.state
        }
      );
    }
  }
  return result;
}

module.exports = {
  parseSitemap: parseSitemap
};
<commit_msg>add todo: find better logging freamework<commit_after>function parseSitemap(sitemap, widgetType) {
  var widgets = sitemap.homepage.widget;
  var result = [];
  for (var i = 0; i < widgets.length; i++) {
    var widget = widgets[i];
    if (!widgetType || widget.type == widgetType) {
      if (!widget.item) {
        // TODO find a better way of logging. Don't log in tests.
        console.log("WARN: The widget '" + widget.label + "' does not reference an item.");
        continue;
      }

      result.push(
        {
          type: widget.type,
          name: widget.label,
          link: widget.item.link,
          state: widget.item.state
        }
      );
    }
  }
  return result;
}

module.exports = {
  parseSitemap: parseSitemap
};
<|endoftext|>


<commit_before>import {GL, Debug, ResizeEvent} from './Context';
import {State as Input} from './Input';
import {glMatrix, vec3, mat4} from 'gl-matrix';
import Mesh from './Mesh';
import Ammo from 'ammo.js';

class Camera {
	constructor(level) {
		/* Camera */
		this.height = 2;
		this.cameraOffset = this.height * 0.425;
		this.fov = glMatrix.toRadian(75);
		this.tilt = glMatrix.toRadian(270);
		this.pitch = glMatrix.toRadian(-15);
		this.projection = mat4.create();
		this.position = vec3.fromValues(Math.floor(Math.random() * 1024) - 1024, this.height * 0.5 + this.cameraOffset, Math.floor(Math.random() * 1024) - 1024);
		this.front = vec3.create();
		this.right = vec3.create();
		this.up = vec3.create();
		this.worldFront = vec3.create();
		this.worldRight = vec3.create();
		this.worldUp = vec3.fromValues(0, 1, 0);
		this.transform = mat4.create();
		this.view = mat4.create();

		/* WebVR */
		if(navigator.getVRDisplays) {
			navigator.getVRDisplays().then((displays) => {
				if(!displays.length) return;
				this.VRDisplay = displays[0];
				this.VRView = mat4.create();
				this.VRProjections = [
					mat4.perspectiveFromFieldOfView(mat4.create(), this.VRDisplay.leftEyeParameters.fieldOfView, this.VRDisplay.depthNear, this.VRDisplay.depthFar),
					mat4.perspectiveFromFieldOfView(mat4.create(), this.VRDisplay.rightEyeParameters.fieldOfView, this.VRDisplay.depthNear, this.VRDisplay.depthFar)
				];
				this.VROffsets = [
					this.VRDisplay.leftEyeParameters.offset,
					this.VRDisplay.rightEyeParameters.offset
				];
				this.VRPositions = [
					vec3.create(),
					vec3.create()
				];
				this.VRTransforms = [
					mat4.create(),
					mat4.create()
				];
			});
		}

		this.onResize = this.onResize.bind(this);
		window.addEventListener(ResizeEvent, this.onResize);
		this.setAspect(GL.drawingBufferWidth / GL.drawingBufferHeight);
		this.updateVectors();

		this.level = level;
		this.auto = true;
	}
	destroy() {
		window.removeEventListener(ResizeEvent, this.onResize);
	}
	onResize() {
		this.setAspect(GL.drawingBufferWidth / GL.drawingBufferHeight);
		this.updateTransform();
	}
	setAspect(aspect) {
		this.aspect = aspect;
		/* Camera projection */
		mat4.perspective(this.projection, this.fov, this.aspect, 0.01, 2000);
	}
	updateVectors() {
		const lookAt = vec3.fromValues(
			Math.cos(this.tilt) * Math.cos(this.pitch),
			Math.sin(this.pitch),
			Math.sin(this.tilt) * Math.cos(this.pitch)
		);

		vec3.normalize(this.front, lookAt);
		vec3.cross(this.right, this.front, this.worldUp);
		vec3.normalize(this.right, this.right);
		vec3.cross(this.up, this.right, this.front);
		vec3.normalize(this.up, this.up);

		vec3.normalize(this.worldFront, vec3.fromValues(lookAt[0], 0, lookAt[2]));
		vec3.cross(this.worldRight, this.worldFront, this.worldUp);
		vec3.normalize(this.worldRight, this.worldRight);

		this.updateTransform();
	}
	updateTransform() {
		if(this.VRDisplay) {
			const VRPosition = vec3.add(vec3.create(), this.position, this.VRDisplay.pose.position);
			mat4.fromRotationTranslation(this.VRView, this.VRDisplay.pose.orientation, VRPosition);
			for(let eye=0; eye<2; eye++) {
				mat4.translate(this.VRTransforms[eye], this.VRView, this.VROffsets[eye]);
				mat4.invert(this.VRTransforms[eye], this.VRTransforms[eye]);
				mat4.multiply(this.VRTransforms[eye], this.VRProjections[eye], this.VRTransforms[eye]);
				vec3.add(this.VRPositions[eye], VRPosition, this.VROffsets[eye]);
			}
		} else {
			const lookAt = vec3.add(vec3.create(), this.position, this.front);
			mat4.lookAt(this.view, this.position, lookAt, this.worldUp);
			mat4.multiply(this.transform, this.projection, this.view);
		}
		Debug && Debug.updatePosition([this.position[0], this.position[1] - this.cameraOffset - this.height * 0.5, this.position[2]]);
	}
	processInput(delta) {
		/* Movement */
		let updatePos = false;
		const speed = Input.flight ? 32 : (this.auto ? 4 : 12);
		const step = speed * delta;

		/* Keyboard */
		Input.flight && (this.auto = true);
		const keystroke = Input.forward || Input.backward || Input.left || Input.right;
		if(this.auto || keystroke) {
			(this.auto || Input.forward) && vec3.scaleAndAdd(this.position, this.position, this.worldFront, step);
			Input.backward && vec3.scaleAndAdd(this.position, this.position, this.worldFront, -step);
			Input.left && vec3.scaleAndAdd(this.position, this.position, this.worldRight, -step);
			Input.right && vec3.scaleAndAdd(this.position, this.position, this.worldRight, step);
			updatePos = true;
			this.auto && keystroke && (this.auto = false);
		}

		/* Pull to floor level */
		const floorY = this.getFloorY();
		const floorDiff = Math.max(floorY + (Input.flight ? 10 : 0), Input.flight ? 30 : 2) - this.position[1];
		if(Math.abs(floorDiff) > 0.0001) {
			this.position[1] += Math.min(Math.max(floorDiff, step * -0.5), floorY > this.position[1] + (Input.flight ? step * 4 : 0) ? floorDiff : step * 0.5);
			updatePos = true;
		}

		if(this.VRDisplay) {
			/* Hacky! */
			const q = this.VRDisplay.pose.orientation;
			this.tilt = -2 * Math.atan2(q[1], q[3]) - Math.PI * 0.5;
			this.pitch = 0;
			return this.updateVectors();
		}

		/* Mouse look */
		if(Input.movementX === 0 && Input.movementY === 0) {
			if(updatePos) return this.updateTransform();
			return;
		}

		const sensitivity = 0.0025;
		const maxTilt = glMatrix.toRadian(360);
		const maxPitch = glMatrix.toRadian(85);

		if(Input.movementX != 0) {
			this.tilt += Input.movementX * sensitivity;
			Input.movementX = 0;
			if(this.tilt < 0) this.tilt += maxTilt;
			if(this.tilt > maxTilt) this.tilt -= maxTilt;
		}

		if(Input.movementY != 0) {
			this.pitch += Input.movementY * -sensitivity;
			Input.movementY = 0;
			if(this.pitch > maxPitch) this.pitch = maxPitch;
			if(this.pitch < -maxPitch) this.pitch = -maxPitch;
		}

		this.updateVectors();
	}
	rayTest(fromX, fromY, fromZ, toX, toY, toZ, mask) {
		let result = false;
		const from = new Ammo.btVector3(this.position[0] + fromX, this.position[1] + fromY, this.position[2] + fromZ);
		const to = new Ammo.btVector3(this.position[0] + toX, this.position[1] + toY, this.position[2] + toZ);
		const rayResult = new Ammo.ClosestRayResultCallback(from, to);
		rayResult.set_m_collisionFilterMask(mask || (Mesh.collisionStatic | Mesh.collisionElevator));
		this.level.world.rayTest(from, to, rayResult);
		if(rayResult.hasHit()) {
			result = rayResult;
		}
		Ammo.destroy(to);
		Ammo.destroy(from);
		return result;
	}
	getFloorY(ceiling) {
		/* Get the closest floor plane at camera position */
		const ray = this.rayTest(
			0, this.height * 0.25 - this.cameraOffset, 0,		// from
			0, 200 * (ceiling ? 1 : -1), 0,									// to
			Mesh.collisionFloor
		);
		if(!ray) return (!ceiling ? this.getFloorY(true) : this.position[1]);
		const floorY = ray.get_m_hitPointWorld().y() + this.height * 0.5 + this.cameraOffset;
		Ammo.destroy(ray);
		return floorY;
	}
	inFustrum(mesh) {
		/* 2D Frustum culling */
		if(!mesh.bounds) return true;
		const origin = vec3.scaleAndAdd(vec3.create(), this.position, this.front, -1);
		for(let i=0; i<mesh.bounds.length; i++) {
			let angle = Math.atan2(mesh.bounds[i][2] - origin[2], mesh.bounds[i][0] - origin[0]) - this.tilt;
			while(angle > Math.PI) angle -= Math.PI * 2;
			while(angle < -Math.PI) angle += Math.PI * 2;
			if(Math.abs(angle) < Math.PI * 0.5) return true;
		}
	}
}

export default Camera;
<commit_msg>slightly lower camera offset<commit_after>import {GL, Debug, ResizeEvent} from './Context';
import {State as Input} from './Input';
import {glMatrix, vec3, mat4} from 'gl-matrix';
import Mesh from './Mesh';
import Ammo from 'ammo.js';

class Camera {
	constructor(level) {
		/* Camera */
		this.height = 2;
		this.cameraOffset = this.height * 0.375;
		this.fov = glMatrix.toRadian(75);
		this.tilt = glMatrix.toRadian(270);
		this.pitch = glMatrix.toRadian(-15);
		this.projection = mat4.create();
		this.position = vec3.fromValues(Math.floor(Math.random() * 1024) - 1024, this.height * 0.5 + this.cameraOffset, Math.floor(Math.random() * 1024) - 1024);
		this.front = vec3.create();
		this.right = vec3.create();
		this.up = vec3.create();
		this.worldFront = vec3.create();
		this.worldRight = vec3.create();
		this.worldUp = vec3.fromValues(0, 1, 0);
		this.transform = mat4.create();
		this.view = mat4.create();

		/* WebVR */
		if(navigator.getVRDisplays) {
			navigator.getVRDisplays().then((displays) => {
				if(!displays.length) return;
				this.VRDisplay = displays[0];
				this.VRView = mat4.create();
				this.VRProjections = [
					mat4.perspectiveFromFieldOfView(mat4.create(), this.VRDisplay.leftEyeParameters.fieldOfView, this.VRDisplay.depthNear, this.VRDisplay.depthFar),
					mat4.perspectiveFromFieldOfView(mat4.create(), this.VRDisplay.rightEyeParameters.fieldOfView, this.VRDisplay.depthNear, this.VRDisplay.depthFar)
				];
				this.VROffsets = [
					this.VRDisplay.leftEyeParameters.offset,
					this.VRDisplay.rightEyeParameters.offset
				];
				this.VRPositions = [
					vec3.create(),
					vec3.create()
				];
				this.VRTransforms = [
					mat4.create(),
					mat4.create()
				];
			});
		}

		this.onResize = this.onResize.bind(this);
		window.addEventListener(ResizeEvent, this.onResize);
		this.setAspect(GL.drawingBufferWidth / GL.drawingBufferHeight);
		this.updateVectors();

		this.level = level;
		this.auto = true;
	}
	destroy() {
		window.removeEventListener(ResizeEvent, this.onResize);
	}
	onResize() {
		this.setAspect(GL.drawingBufferWidth / GL.drawingBufferHeight);
		this.updateTransform();
	}
	setAspect(aspect) {
		this.aspect = aspect;
		/* Camera projection */
		mat4.perspective(this.projection, this.fov, this.aspect, 0.01, 2000);
	}
	updateVectors() {
		const lookAt = vec3.fromValues(
			Math.cos(this.tilt) * Math.cos(this.pitch),
			Math.sin(this.pitch),
			Math.sin(this.tilt) * Math.cos(this.pitch)
		);

		vec3.normalize(this.front, lookAt);
		vec3.cross(this.right, this.front, this.worldUp);
		vec3.normalize(this.right, this.right);
		vec3.cross(this.up, this.right, this.front);
		vec3.normalize(this.up, this.up);

		vec3.normalize(this.worldFront, vec3.fromValues(lookAt[0], 0, lookAt[2]));
		vec3.cross(this.worldRight, this.worldFront, this.worldUp);
		vec3.normalize(this.worldRight, this.worldRight);

		this.updateTransform();
	}
	updateTransform() {
		if(this.VRDisplay) {
			const VRPosition = vec3.add(vec3.create(), this.position, this.VRDisplay.pose.position);
			mat4.fromRotationTranslation(this.VRView, this.VRDisplay.pose.orientation, VRPosition);
			for(let eye=0; eye<2; eye++) {
				mat4.translate(this.VRTransforms[eye], this.VRView, this.VROffsets[eye]);
				mat4.invert(this.VRTransforms[eye], this.VRTransforms[eye]);
				mat4.multiply(this.VRTransforms[eye], this.VRProjections[eye], this.VRTransforms[eye]);
				vec3.add(this.VRPositions[eye], VRPosition, this.VROffsets[eye]);
			}
		} else {
			const lookAt = vec3.add(vec3.create(), this.position, this.front);
			mat4.lookAt(this.view, this.position, lookAt, this.worldUp);
			mat4.multiply(this.transform, this.projection, this.view);
		}
		Debug && Debug.updatePosition([this.position[0], this.position[1] - this.cameraOffset - this.height * 0.5, this.position[2]]);
	}
	processInput(delta) {
		/* Movement */
		let updatePos = false;
		const speed = Input.flight ? 32 : (this.auto ? 4 : 12);
		const step = speed * delta;

		/* Keyboard */
		Input.flight && (this.auto = true);
		const keystroke = Input.forward || Input.backward || Input.left || Input.right;
		if(this.auto || keystroke) {
			(this.auto || Input.forward) && vec3.scaleAndAdd(this.position, this.position, this.worldFront, step);
			Input.backward && vec3.scaleAndAdd(this.position, this.position, this.worldFront, -step);
			Input.left && vec3.scaleAndAdd(this.position, this.position, this.worldRight, -step);
			Input.right && vec3.scaleAndAdd(this.position, this.position, this.worldRight, step);
			updatePos = true;
			this.auto && keystroke && (this.auto = false);
		}

		/* Pull to floor level */
		const floorY = this.getFloorY();
		const floorDiff = Math.max(floorY + (Input.flight ? 10 : 0), Input.flight ? 30 : 2) - this.position[1];
		if(Math.abs(floorDiff) > 0.0001) {
			this.position[1] += Math.min(Math.max(floorDiff, step * -0.5), floorY > this.position[1] + (Input.flight ? step * 4 : 0) ? floorDiff : step * 0.5);
			updatePos = true;
		}

		if(this.VRDisplay) {
			/* Hacky! */
			const q = this.VRDisplay.pose.orientation;
			this.tilt = -2 * Math.atan2(q[1], q[3]) - Math.PI * 0.5;
			this.pitch = 0;
			return this.updateVectors();
		}

		/* Mouse look */
		if(Input.movementX === 0 && Input.movementY === 0) {
			if(updatePos) return this.updateTransform();
			return;
		}

		const sensitivity = 0.0025;
		const maxTilt = glMatrix.toRadian(360);
		const maxPitch = glMatrix.toRadian(85);

		if(Input.movementX != 0) {
			this.tilt += Input.movementX * sensitivity;
			Input.movementX = 0;
			if(this.tilt < 0) this.tilt += maxTilt;
			if(this.tilt > maxTilt) this.tilt -= maxTilt;
		}

		if(Input.movementY != 0) {
			this.pitch += Input.movementY * -sensitivity;
			Input.movementY = 0;
			if(this.pitch > maxPitch) this.pitch = maxPitch;
			if(this.pitch < -maxPitch) this.pitch = -maxPitch;
		}

		this.updateVectors();
	}
	rayTest(fromX, fromY, fromZ, toX, toY, toZ, mask) {
		let result = false;
		const from = new Ammo.btVector3(this.position[0] + fromX, this.position[1] + fromY, this.position[2] + fromZ);
		const to = new Ammo.btVector3(this.position[0] + toX, this.position[1] + toY, this.position[2] + toZ);
		const rayResult = new Ammo.ClosestRayResultCallback(from, to);
		rayResult.set_m_collisionFilterMask(mask || (Mesh.collisionStatic | Mesh.collisionElevator));
		this.level.world.rayTest(from, to, rayResult);
		if(rayResult.hasHit()) {
			result = rayResult;
		}
		Ammo.destroy(to);
		Ammo.destroy(from);
		return result;
	}
	getFloorY(ceiling) {
		/* Get the closest floor plane at camera position */
		const ray = this.rayTest(
			0, this.height * 0.25 - this.cameraOffset, 0,		// from
			0, 200 * (ceiling ? 1 : -1), 0,									// to
			Mesh.collisionFloor
		);
		if(!ray) return (!ceiling ? this.getFloorY(true) : this.position[1]);
		const floorY = ray.get_m_hitPointWorld().y() + this.height * 0.5 + this.cameraOffset;
		Ammo.destroy(ray);
		return floorY;
	}
	inFustrum(mesh) {
		/* 2D Frustum culling */
		if(!mesh.bounds) return true;
		const origin = vec3.scaleAndAdd(vec3.create(), this.position, this.front, -1);
		for(let i=0; i<mesh.bounds.length; i++) {
			let angle = Math.atan2(mesh.bounds[i][2] - origin[2], mesh.bounds[i][0] - origin[0]) - this.tilt;
			while(angle > Math.PI) angle -= Math.PI * 2;
			while(angle < -Math.PI) angle += Math.PI * 2;
			if(Math.abs(angle) < Math.PI * 0.5) return true;
		}
	}
}

export default Camera;
<|endoftext|>


<commit_before>'use strict';

angular.module('myApp.Events', ['ngRoute'])

.controller('EventsCtrl', function($scope, EventsFactory, $routeParams, GroundsFactory) {

    $scope.event = {};
    $scope.ground = {};

    if($routeParams.idEvent != null){
        EventsFactory.getEvent($routeParams.idEvent).success(function (event) {
            $scope.event = event[0];
            //alert(event[0].groundID);
            GroundsFactory.getGround(event[0].groundID).success(function (ground) {
                $scope.ground = ground;
            });
        });
    }

    loadEvents();

    function loadEvents(){
        EventsFactory.events().success(function (response) {
            $scope.events = response;
        });
    }
});

<commit_msg>events<commit_after>'use strict';

angular.module('myApp.Events', ['ngRoute'])

.controller('EventsCtrl', function($scope, EventsFactory, $routeParams, GroundsFactory) {

    $scope.event = {};
    $scope.ground = {};

    if($routeParams.idEvent != null){
        EventsFactory.getEvent($routeParams.idEvent).success(function (event) {
            $scope.event = event[0];
            GroundsFactory.getGround(event[0].groundID).success(function (ground) {
                $scope.ground = ground;
            });
        });
    }

    loadEvents();

    function loadEvents(){
        EventsFactory.events().success(function (response) {

            for(var i = 0; i < response.length; i++){
                var date = new Date(response[i].date);
                console.log(date);
            }

            $scope.events = response;
        });
    }
});

<|endoftext|>


<commit_before>// * phrase search
// * html teaser with tagged match

module.exports = function (options) {

    var SearchIndex = require('search-index');
    var path = require('path');

    var preparer = require('./Preparer.js');
    var cfi = require('./CFI.js');

    var indexDB = 'indexDB'; // path to index-db
    var defaultOption = {'indexPath': indexDB, logLevel: 'warn'};
    options = options || defaultOption;

    var si = new SearchIndex(options);
    var that = this;

    this.indexing = function (pathToEpubs, callback) {

        console.log("normalize epub content");

        path.normalize(pathToEpubs);

        preparer.normalize(pathToEpubs, function (dataSet) {

            console.log("start indexing");
            that.add(dataSet, function (err) {
                //console.log(dataSet);
                if (callback) {
                    if (err)
                        callback(err);
                    else
                        callback('all is indexed');
                }
            });
            //callback('all is indexed');
        });
    };

    this.add = function (jsonDoc, callback) {

        var options = {};
        options.filters = [];

        //console.debug(si.indexes);
        //si.get(jsonDoc.epubTitle, function (err, doc) {
        // // update doc with latest version ???
        //});

        si.add(options, jsonDoc, callback);
    };

    this.search = function (q, epubTitle, callback) {

        epubTitle = epubTitle || '*'; // if epubTitle undefined return all hits

        // q is an array !!!
        var query = {
            "query": {"*": q},
            "offset": 0,
            "pageSize": 100
        };

        si.search(query, function (err, result) {

            if (result.hits) {

                var hits = [];
                for (var i in result.hits) {

                    var title = result.hits[i].document.epubTitle;
                    if (title === epubTitle || epubTitle === '*') {

                        var data = {
                            "query": q,
                            "spineItemPath": result.hits[i].document.spineItemPath,
                            "baseCfi": result.hits[i].document.baseCfi
                        }

                        var cfiList = cfi.generate(data);

                        if (cfiList.length > 0) {
                            result.hits[i].document.cfis = cfiList;
                            delete result.hits[i].document.body;
                            delete result.hits[i].document.title;
                            delete result.hits[i].document['*'];
                            delete result.hits[i].document.spineItemPath;

                            hits.push(result.hits[i].document);
                        }
                    }
                }
                callback(hits);
            }
        })
    };

    this.match = function (beginsWith, callback) {
        si.match(beginsWith, callback);
    };

    this.empty = function (callback) {
        si.empty(callback);
    };
};<commit_msg>- added contract to check epub data folder is not empty - added close()<commit_after>// * phrase search
// * html teaser with tagged match

module.exports = function (options) {

    var SearchIndex = require('search-index');
    var path = require('path');
    var fs = require('extfs');

    var preparer = require('./Preparer.js');
    var cfi = require('./CFI.js');

    var indexDB = 'indexDB'; // path to index-db
    var defaultOption = {'indexPath': indexDB, logLevel: 'warn'};
    options = options || defaultOption;

    var si = new SearchIndex(options);
    var that = this;

    this.indexing = function (pathToEpubs, callback) {

        if(fs.isEmptySync(pathToEpubs))
          return callback(new Error('Can`t index empty folder: ' + pathToEpubs));

        console.log("normalize epub content");

        path.normalize(pathToEpubs);

        preparer.normalize(pathToEpubs, function (dataSet) {

            console.log("start indexing");
            that.add(dataSet, function (err) {
                //console.log(dataSet);
                if (callback) {
                    if (err)
                        callback(err);
                    else
                        callback('all is indexed');
                }
            });
            //callback('all is indexed');
        });
    };

    this.add = function (jsonDoc, callback) {

        var options = {};
        options.filters = [];

        //console.debug(si.indexes);
        //si.get(jsonDoc.epubTitle, function (err, doc) {
        // // update doc with latest version ???
        //});

        si.add(options, jsonDoc, callback);
    };

    this.search = function (q, epubTitle, callback) {

        epubTitle = epubTitle || '*'; // if epubTitle undefined return all hits

        // q is an array !!!
        var query = {
            "query": {"*": q},
            "offset": 0,
            "pageSize": 100
        };

        si.search(query, function (err, result) {

            if (result.hits) {

                var hits = [];
                for (var i in result.hits) {

                    var title = result.hits[i].document.epubTitle;
                    if (title === epubTitle || epubTitle === '*') {

                        var data = {
                            "query": q,
                            "spineItemPath": result.hits[i].document.spineItemPath,
                            "baseCfi": result.hits[i].document.baseCfi
                        }

                        var cfiList = cfi.generate(data);

                        if (cfiList.length > 0) {
                            result.hits[i].document.cfis = cfiList;
                            delete result.hits[i].document.body;
                            delete result.hits[i].document.title;
                            delete result.hits[i].document['*'];
                            delete result.hits[i].document.spineItemPath;

                            hits.push(result.hits[i].document);
                        }
                    }
                }
                callback(hits);
            }
        })
    };

    this.match = function (beginsWith, callback) {
        si.match(beginsWith, callback);
    };

    this.empty = function (callback) {
        si.empty(callback);
    };

    this.close = function(callback) {
        si.close(callback);
    }
};<|endoftext|>


<commit_before>import axios from "axios";
import R from "ramda";

import {WRITE_API_ENDPOINT} from "lib/config";
import {getChildren, getUsername, isActiveUser} from "lib/users-utils";

import {getBasicObject} from "./utils";

export const ADD_ROLE = "ADD_ROLE";
export const REMOVE_ROLE = "REMOVE_ROLE";
export const RESET_ROLES_GROUPS = "RESET_ROLES_GROUPS";
export const SELECT_USER = "SELECT_USER";
export const TOGGLE_GROUP = "TOGGLE_GROUP";
export const USER_DELETE_SUCCESS = "USER_DELETE_SUCCESS";

export const selectUser = user => getBasicObject(SELECT_USER, user);

export const changeActiveStatus = user => {
    return dispatch => {
        dispatch({
            type: "CHANGING_ACTIVE_STATUS"
        });
        const newProfile = {
            profile: {
                ...user.get("profile").toObject(),
                active: !isActiveUser(user)
            }
        };
        updateUser(dispatch, user, newProfile, "ACTIVE_STATUS_UPDATE");
    };
};

function updateUser (dispatch, user, updatedInfo, event) {
    const userName = getUsername(user);
    const endpoint = "http://" + WRITE_API_ENDPOINT + "/users/";
    const updatedUser = {
        uid: userName,
        ...updatedInfo
    };
    axios.post(endpoint, updatedUser)
        .then(() => dispatch({
            type: event + "_SUCCESS"
        }))
        .catch(() => dispatch({
            type: event + "_FAIL"
        }));
}

export const assignSensorsToUsers = (users, sensors) => {
    return dispatch => {
        dispatch({
            type: "ASSIGNING_SENSORS_TO_USERS"
        });
        checkAndAssignObjectsToUsers(dispatch, users, sensors, assignSensorsToUser, "sensors");
    };
};

function checkAndAssignObjectsToUsers (dispatch, users, objects, assignFunc, userField) {
    if (users.length == 1) {
        assignFunc(dispatch, users[0], objects);
    } else {
        users.forEach(user => {
            const merged = user.get(userField) ? R.compose(R.uniq, R.flatten)([user.get(userField).toJS(), objects]) : objects;
            assignFunc(dispatch, user, merged);
        });
    }
}

function assignSensorsToUser (dispatch, user, sensors) {
    const updated = {
        sensors: sensors
    };
    updateUser(dispatch, user, updated, "SENSORS_ASSIGNMENT");
}

function assignGroupsToUser (dispatch, user, groups) {
    const updated = {
        groups: groups
    };
    updateUser(dispatch, user, updated, "GROUPS_ASSIGNMENT");
}

export const deleteUsers = (usersToDelete, allUsers) => {
    return dispatch => {
        dispatch({
            type: "DELETING_USERS"
        });
        usersToDelete.forEach(user => deleteUser(user, allUsers, dispatch));
    };
};

function deleteUser (user, allUsers, dispatch) {
    const userId = user.get("_id");
    const endpoint = "http://" + WRITE_API_ENDPOINT + "/users/" + userId;
    axios.delete(endpoint)
        .then(() => dispatch({
            type: USER_DELETE_SUCCESS,
            payload: userId
        }))
        .catch(() => dispatch({
            type: "USER_DELETE_FAIL"
        }));
    getChildren(userId, allUsers).forEach(child => deleteUser(child, allUsers, dispatch));
}

export const toggleGroup = group => getBasicObject(TOGGLE_GROUP, group);

export const addRole = role => getBasicObject(ADD_ROLE, role);

export const removeRole = role => getBasicObject(REMOVE_ROLE, role);

export const resetRolesAndGroups = () => getBasicObject(RESET_ROLES_GROUPS);

export const assignGroupsToUsers = (users, groups) => {
    return dispatch => {
        dispatch({
            type: "ASSIGNING_GROUPS_TO_USERS"
        });
        checkAndAssignObjectsToUsers(dispatch, users, groups, assignGroupsToUser, "groups");
    };
};

export const saveAndAssignGroupToUsers = (users, groupName, roles) => {
    return dispatch => {
        dispatch({
            type: "ASSIGNING_GROUPS_TO_USERS"
        });
        var endpoint = "http://" + WRITE_API_ENDPOINT + "/groups";
        const group = {
            name: groupName,
            roles: roles
        };
        axios.post(endpoint, group)
            .then(() => dispatch({
                type: "GROUP_CREATION_SUCCESS"
            }))
            .catch(() => dispatch({
                type: "GROUP_CREATION_FAIL"
            }));
        checkAndAssignObjectsToUsers(dispatch, users, [groupName], assignGroupsToUser, "groups");
    };
};

export const moveUser = (user, parentUserId) => {
    return dispatch => {
        dispatch({
            type: "MOVING_USER"
        });
        const newProfile = {
            profile: {
                ...user.get("profile").toObject(),
                parentUserId: parentUserId
            }
        };
        updateUser(dispatch, user, newProfile, "CHANGE_USER_PARENT");
    };
};
<commit_msg>fix delete of users<commit_after>import axios from "axios";
import R from "ramda";

import {WRITE_API_ENDPOINT} from "lib/config";
import {getChildren, getUsername, isActiveUser} from "lib/users-utils";

import {getBasicObject} from "./utils";

export const ADD_ROLE = "ADD_ROLE";
export const REMOVE_ROLE = "REMOVE_ROLE";
export const RESET_ROLES_GROUPS = "RESET_ROLES_GROUPS";
export const SELECT_USER = "SELECT_USER";
export const TOGGLE_GROUP = "TOGGLE_GROUP";

export const selectUser = user => getBasicObject(SELECT_USER, user);

export const changeActiveStatus = user => {
    return dispatch => {
        dispatch({
            type: "CHANGING_ACTIVE_STATUS"
        });
        const newProfile = {
            profile: {
                ...user.get("profile").toObject(),
                active: !isActiveUser(user)
            }
        };
        updateUser(dispatch, user, newProfile, "ACTIVE_STATUS_UPDATE");
    };
};

function updateUser (dispatch, user, updatedInfo, event) {
    const userName = getUsername(user);
    const endpoint = "http://" + WRITE_API_ENDPOINT + "/users/";
    const updatedUser = {
        uid: userName,
        ...updatedInfo
    };
    axios.post(endpoint, updatedUser)
        .then(() => dispatch({
            type: event + "_SUCCESS"
        }))
        .catch(() => dispatch({
            type: event + "_FAIL"
        }));
}

export const assignSensorsToUsers = (users, sensors) => {
    return dispatch => {
        dispatch({
            type: "ASSIGNING_SENSORS_TO_USERS"
        });
        checkAndAssignObjectsToUsers(dispatch, users, sensors, assignSensorsToUser, "sensors");
    };
};

function checkAndAssignObjectsToUsers (dispatch, users, objects, assignFunc, userField) {
    if (users.length == 1) {
        assignFunc(dispatch, users[0], objects);
    } else {
        users.forEach(user => {
            const merged = user.get(userField) ? R.compose(R.uniq, R.flatten)([user.get(userField).toJS(), objects]) : objects;
            assignFunc(dispatch, user, merged);
        });
    }
}

function assignSensorsToUser (dispatch, user, sensors) {
    const updated = {
        sensors: sensors
    };
    updateUser(dispatch, user, updated, "SENSORS_ASSIGNMENT");
}

function assignGroupsToUser (dispatch, user, groups) {
    const updated = {
        groups: groups
    };
    updateUser(dispatch, user, updated, "GROUPS_ASSIGNMENT");
}

export const deleteUsers = (usersToDelete, allUsers) => {
    return dispatch => {
        dispatch({
            type: "DELETING_USERS"
        });
        usersToDelete.forEach(user => deleteUser(user, allUsers, dispatch));
    };
};

function deleteUser (user, allUsers, dispatch) {
    const userId = user.get("_id");
    const newProfile = {
        profile: {
            ...user.get("profile").toObject(),
            isDeleted: true
        }
    };
    updateUser(dispatch, user, newProfile, "USER_DELETE");
    getChildren(userId, allUsers).forEach(child => deleteUser(child, allUsers, dispatch));
}

export const toggleGroup = group => getBasicObject(TOGGLE_GROUP, group);

export const addRole = role => getBasicObject(ADD_ROLE, role);

export const removeRole = role => getBasicObject(REMOVE_ROLE, role);

export const resetRolesAndGroups = () => getBasicObject(RESET_ROLES_GROUPS);

export const assignGroupsToUsers = (users, groups) => {
    return dispatch => {
        dispatch({
            type: "ASSIGNING_GROUPS_TO_USERS"
        });
        checkAndAssignObjectsToUsers(dispatch, users, groups, assignGroupsToUser, "groups");
    };
};

export const saveAndAssignGroupToUsers = (users, groupName, roles) => {
    return dispatch => {
        dispatch({
            type: "ASSIGNING_GROUPS_TO_USERS"
        });
        var endpoint = "http://" + WRITE_API_ENDPOINT + "/groups";
        const group = {
            name: groupName,
            roles: roles
        };
        axios.post(endpoint, group)
            .then(() => dispatch({
                type: "GROUP_CREATION_SUCCESS"
            }))
            .catch(() => dispatch({
                type: "GROUP_CREATION_FAIL"
            }));
        checkAndAssignObjectsToUsers(dispatch, users, [groupName], assignGroupsToUser, "groups");
    };
};

export const moveUser = (user, parentUserId) => {
    return dispatch => {
        dispatch({
            type: "MOVING_USER"
        });
        const newProfile = {
            profile: {
                ...user.get("profile").toObject(),
                parentUserId: parentUserId
            }
        };
        updateUser(dispatch, user, newProfile, "CHANGE_USER_PARENT");
    };
};
<|endoftext|>


<commit_before>CATS.Adapter.Ifmo = Classify({
    init : function(page) {
        if (page != undefined)
            this.page = page;

        this.name = "ifmo";
        this.aliases = {
            'rankl' : 'place',
            'rank' : 'place',
            'party' : 'user',
            'penalty' : 'penalty'
        }
    },

    parse_score_board: function(contest_id, result_table) {
        var page = this.page;
        var contest = CATS.App.contests[contest_id];
        if (contest == undefined)
            contest = this.add_contest({id: contest_id, name: "NEERC " + contest_id});

        var self = this;
        var problem_list = [];
        $(page).find('table[class != "wrapper"]').find("tr").each(function () {
            var prob_num = 0;
            $(this).find("th.problem").each(function () {
                var prob = new CATS.Model.Problem();
                prob.id = prob.name = $(this).attr("title");
                prob.code = $(this).html();
                CATS.App.add_object(prob);
                contest.add_object(prob);
                problem_list.push($(this).attr("title"));
            });

            var parse_row = false;
            var row = result_table.get_empty_score_board_row();
            $(this).find("td").each(function () {
                var key = $(this).attr('class');
                if (key !== undefined) {
                    parse_row = true;
                    if (self.aliases[key] != undefined)
                        row[self.aliases[key]] = $(this).html();
                } else {
                    var runs = $(this).find("i");
                    runs = runs.length == 0 ? $(this).find("b") : runs;
                    var solved = -1, runs_cnt = -1;
                    if (runs.length > 0) {

                        solved = $(runs).html().indexOf("+") != -1;
                        var time = $(runs).find("s");
                        if (time.length > 0) {
                            time.find("br").remove();
                            solved = parseInt(time.html().split(":")[0]);
                            time.remove();
                        }
                        runs_cnt = $(runs).html();
                        if (runs_cnt.length == 1)
                            runs_cnt = 0;
                        runs_cnt = Math.abs(parseInt(runs_cnt));
                    }
                    else if ($(this).html() == ".") {
                        solved = false;
                        runs_cnt = 0;
                    }

                    if (solved != -1) {
                        var prob = result_table.get_empty_problem_for_score_board_row();
                        prob['problem'] = problem_list[prob_num++];
                        prob['is_solved'] = solved != false;
                        if (prob['is_solved']) {
                            prob['best_run_time'] = solved;
                            row['solved_cnt']++;
                        }
                        prob['runs_cnt'] = solved ? runs_cnt + 1 : runs_cnt;

                        row['problems'].push(prob);
                    }
                }
            });
            if (parse_row) {
                row['place'] = parseInt(row['place']);
                row['penalty'] = parseInt(row['penalty']);

                var user = new CATS.Model.User();
                user.name = user.id = row['user'];
                CATS.App.add_object(user);
                contest.add_object(user);

                result_table.score_board.push(row);
            }
        });
    },

    add_contest: function(v) {
        var contest = CATS.Model.Contest(), contests = [];
        contest.id = v.id;
        contest.name = v.name;
        contest.scoring = "acm";
        contest.start_time = new Date();
        contest.finish_time = CATS.App.utils.add_time(contest.start_time, 300);
        CATS.App.add_object(contest);
        return contest;
    },

    get_contests: function(callback) {
        var self = this;
        CATS.App.utils.proxy_get("http://neerc.ifmo.ru/past/index.html", function (page) {
            var contests = [];
            $(page).find('td[class = "neercyear"]').find("a").each(function () {
                var c = {id: $(this).html().match(/\d+/g)[0], name: $(this).html()};
                self.add_contest(c);
                contests.push(c.id);
            });
            callback(contests);
        })
    },

    get_contest: function(callback, contest_id) {
        var self = this;
        CATS.App.utils.proxy_get("http://neerc.ifmo.ru/past/" + contest_id + "/standings.html", function (page) {
            callback(page);
        })
    },

    parse: function(contest_id, result_table, callback) {
        var self = this;
        this.get_contest(function (page) {
            self.page = page;
            self.parse_score_board(contest_id, result_table);
            callback();
        }, contest_id);
    }
});
<commit_msg>adapters/ifmo: Ifmo now supports Access-Control-Allow-Origin<commit_after>CATS.Adapter.Ifmo = Classify({
    init : function(page) {
        if (page != undefined)
            this.page = page;

        this.name = "ifmo";
        this.aliases = {
            'rankl' : 'place',
            'rank' : 'place',
            'party' : 'user',
            'penalty' : 'penalty'
        }
    },

    parse_score_board: function(contest_id, result_table) {
        var page = this.page;
        var contest = CATS.App.contests[contest_id];
        if (contest == undefined)
            contest = this.add_contest({id: contest_id, name: "NEERC " + contest_id});

        var self = this;
        var problem_list = [];
        $(page).find('table[class != "wrapper"]').find("tr").each(function () {
            var prob_num = 0;
            $(this).find("th.problem").each(function () {
                var prob = new CATS.Model.Problem();
                prob.id = prob.name = $(this).attr("title");
                prob.code = $(this).html();
                CATS.App.add_object(prob);
                contest.add_object(prob);
                problem_list.push($(this).attr("title"));
            });

            var parse_row = false;
            var row = result_table.get_empty_score_board_row();
            $(this).find("td").each(function () {
                var key = $(this).attr('class');
                if (key !== undefined) {
                    parse_row = true;
                    if (self.aliases[key] != undefined)
                        row[self.aliases[key]] = $(this).html();
                } else {
                    var runs = $(this).find("i");
                    runs = runs.length == 0 ? $(this).find("b") : runs;
                    var solved = -1, runs_cnt = -1;
                    if (runs.length > 0) {

                        solved = $(runs).html().indexOf("+") != -1;
                        var time = $(runs).find("s");
                        if (time.length > 0) {
                            time.find("br").remove();
                            solved = parseInt(time.html().split(":")[0]);
                            time.remove();
                        }
                        runs_cnt = $(runs).html();
                        if (runs_cnt.length == 1)
                            runs_cnt = 0;
                        runs_cnt = Math.abs(parseInt(runs_cnt));
                    }
                    else if ($(this).html() == ".") {
                        solved = false;
                        runs_cnt = 0;
                    }

                    if (solved != -1) {
                        var prob = result_table.get_empty_problem_for_score_board_row();
                        prob['problem'] = problem_list[prob_num++];
                        prob['is_solved'] = solved != false;
                        if (prob['is_solved']) {
                            prob['best_run_time'] = solved;
                            row['solved_cnt']++;
                        }
                        prob['runs_cnt'] = solved ? runs_cnt + 1 : runs_cnt;

                        row['problems'].push(prob);
                    }
                }
            });
            if (parse_row) {
                row['place'] = parseInt(row['place']);
                row['penalty'] = parseInt(row['penalty']);

                var user = new CATS.Model.User();
                user.name = user.id = row['user'];
                CATS.App.add_object(user);
                contest.add_object(user);

                result_table.score_board.push(row);
            }
        });
    },

    add_contest: function(v) {
        var contest = CATS.Model.Contest(), contests = [];
        contest.id = v.id;
        contest.name = v.name;
        contest.scoring = "acm";
        contest.start_time = new Date();
        contest.finish_time = CATS.App.utils.add_time(contest.start_time, 300);
        CATS.App.add_object(contest);
        return contest;
    },

    url: 'http://neerc.ifmo.ru/',

    get_contests: function(callback) {
        var self = this;
        CATS.App.utils.cors_get_html(this.url + 'past/index.html', function (page) {
            var contests = [];
            $(page).find('td[class = "neercyear"]').find("a").each(function () {
                var c = {id: $(this).html().match(/\d+/g)[0], name: $(this).html()};
                self.add_contest(c);
                contests.push(c.id);
            });
            callback(contests);
        })
    },

    get_contest: function(callback, contest_id) {
        var self = this;
        CATS.App.utils.cors_get_html(this.url + 'past/' + contest_id + '/standings.html', function (page) {
            callback(page);
        });
    },

    parse: function(contest_id, result_table, callback) {
        var self = this;
        this.get_contest(function (page) {
            self.page = page;
            self.parse_score_board(contest_id, result_table);
            callback();
        }, contest_id);
    }
});
<|endoftext|>