<commit_before>import * as types from '../constants/action_types';
import Octokat from 'octokat';
import { Base64 } from 'js-base64';
let client, repo, config = {};

function setActor(actor) {
  return {
    type: types.ACTOR,
    actor
  };
}

function setPeople(people) {
  return {
    type: types.PEOPLE,
    people
  };
}

function setForm(form) {
  return {
    type: types.FORM,
    form
  };
}

export function setMessage(message) {
  return {
    type: types.MESSAGE,
    message
  };
}

export function setError(error) {
  return {
    type: types.ERROR,
    error
  };
}

export function setValidators(validators) {
  return {
    type: types.VALIDATORS,
    validators
  };
}

export function setListingRenderer(listingTemplate) {
  return {
    type: types.LISTING_TEMPLATE,
    listingTemplate
  };
}

export function setNormalizers(normalizers) {
  return {
    type: types.NORMALIZERS,
    normalizers
  };
}

export function setOptions(options) {
  client = new Octokat({ token: options.GitHubToken });
  repo = client.repos(options.org, options.repo);

  return {
    type: types.OPTIONS,
    options
  }
}

export function addUser(obj, cb) {
  return (dispatch, getState) => {
    const { options, people } = getState().directory;
    repo.contents(options.people).fetch().then((res) => {

      const dataFromGitHub = JSON.parse(Base64.decode(res.content));
      dataFromGitHub.push(obj); // New record

      const payload = JSON.stringify(dataFromGitHub, null, 2) + '\n';
      const putData = {
        message: 'Created ' + obj.github,
        content: Base64.encode(payload),
        sha: res.sha
      };

      repo.contents(options.people).add(putData)
        .then(() => {
          dispatch(setPeople(dataFromGitHub));
          cb(null);
        }).catch((err) => { cb(err); });
    }).catch((err) => { cb(err); });
  }
}

export function updateUser(obj, cb) {
  return (dispatch, getState) => {
    const { options, people } = getState().directory;
    repo.contents(options.people).fetch().then((res) => {

      const dataFromGitHub = JSON.parse(Base64.decode(res.content)).map((d) => {
        if (obj.github.toLowerCase() === d.github.toLowerCase()) d = obj;
        return d;
      });

      const payload = JSON.stringify(dataFromGitHub, null, 2) + '\n';
      const putData = {
        message: 'Updated ' + obj.github,
        content: Base64.encode(payload),
        sha: res.sha
      };

      repo.contents(options.people).add(putData)
        .then(() => {
          dispatch(setPeople(dataFromGitHub));
          cb(null);
        }).catch((err) => { cb(err); });
    }).catch((err) => { cb(err); });
  }
}

export function removeUser(username, cb) {
  return (dispatch, getState) => {
    const { options, people } = getState().directory;
    repo.contents(options.people).fetch().then((res) => {

      const dataFromGitHub = JSON.parse(Base64.decode(res.content)).filter((d) => {
        return username.toLowerCase() !== d.github.toLowerCase();
      });

      const payload = JSON.stringify(dataFromGitHub, null, 2) + '\n';
      const putData = {
        message: 'Removed ' + username,
        content: Base64.encode(payload),
        sha: res.sha
      };

      repo.contents(options.people).add(putData)
        .then(() => {
          dispatch(setPeople(dataFromGitHub));
          cb(null);
        }).catch((err) => { cb(err); });
      }).catch((err) => { cb(err); });
  }
}

export function loadForm() {
  return (dispatch, getState) => {
    const { options } = getState().directory;
    repo.contents(options.form).read()
      .then((res) => {
        res = JSON.parse(res);
        let data = [];
        for (const prop in res) {
          data.push({
            section: prop,
            data: res[prop]
          });
        }
        dispatch(setForm(data));
      })
      .catch((err) => {
        console.log('whats the error?', err);
        dispatch(setForm([]));
      });
  };
}

export function loadUser(u) {
  return (dispatch, getState) => {
    const { people } = getState().directory;
    const user = people.filter((d) => {
      return u.toLowerCase() === d.github.toLowerCase();
    })[0];

    dispatch({
      type: types.USER,
      user
    });
  };
}

export function loadPeople(query) {
  const filter = (query && query.filter) ? query.filter : null;
  const sort = (query && query.sort) ? query.sort : 'name';

  return (dispatch, getState) => {
    const { options } = getState().directory;
    client.user.fetch()
      .then((user) => {
        dispatch(setActor(user));
        repo.contents(options.people).read()
          .then((data) => {
            dispatch(setPeople(JSON.parse(data)));
          })
          .catch((err) => {
            console.log('An error occurred', err);
            dispatch(setPeople([]));
          });
      });
  };
}

export function dismissError() {
  return (dispatch) => {
    dispatch(setError(''));
  };
}

export function dismissModal() {
  return (dispatch) => {
    dispatch(setMessage(''));
  };
}
<commit_msg>Add an admin key to actor object if admin key is set in current users object<commit_after>import * as types from '../constants/action_types';
import Octokat from 'octokat';
import { Base64 } from 'js-base64';
let client, repo, config = {};

function setActor(actor) {
  return {
    type: types.ACTOR,
    actor
  };
}

function setPeople(people) {
  return {
    type: types.PEOPLE,
    people
  };
}

function setForm(form) {
  return {
    type: types.FORM,
    form
  };
}

export function setMessage(message) {
  return {
    type: types.MESSAGE,
    message
  };
}

export function setError(error) {
  return {
    type: types.ERROR,
    error
  };
}

export function setValidators(validators) {
  return {
    type: types.VALIDATORS,
    validators
  };
}

export function setListingRenderer(listingTemplate) {
  return {
    type: types.LISTING_TEMPLATE,
    listingTemplate
  };
}

export function setNormalizers(normalizers) {
  return {
    type: types.NORMALIZERS,
    normalizers
  };
}

export function setOptions(options) {
  client = new Octokat({ token: options.GitHubToken });
  repo = client.repos(options.org, options.repo);

  return {
    type: types.OPTIONS,
    options
  }
}

export function addUser(obj, cb) {
  return (dispatch, getState) => {
    const { options, people } = getState().directory;
    repo.contents(options.people).fetch().then((res) => {

      const dataFromGitHub = JSON.parse(Base64.decode(res.content));
      dataFromGitHub.push(obj); // New record

      const payload = JSON.stringify(dataFromGitHub, null, 2) + '\n';
      const putData = {
        message: 'Created ' + obj.github,
        content: Base64.encode(payload),
        sha: res.sha
      };

      repo.contents(options.people).add(putData)
        .then(() => {
          dispatch(setPeople(dataFromGitHub));
          cb(null);
        }).catch((err) => { cb(err); });
    }).catch((err) => { cb(err); });
  }
}

export function updateUser(obj, cb) {
  return (dispatch, getState) => {
    const { options, people } = getState().directory;
    repo.contents(options.people).fetch().then((res) => {

      const dataFromGitHub = JSON.parse(Base64.decode(res.content)).map((d) => {
        if (obj.github.toLowerCase() === d.github.toLowerCase()) d = obj;
        return d;
      });

      const payload = JSON.stringify(dataFromGitHub, null, 2) + '\n';
      const putData = {
        message: 'Updated ' + obj.github,
        content: Base64.encode(payload),
        sha: res.sha
      };

      repo.contents(options.people).add(putData)
        .then(() => {
          dispatch(setPeople(dataFromGitHub));
          cb(null);
        }).catch((err) => { cb(err); });
    }).catch((err) => { cb(err); });
  }
}

export function removeUser(username, cb) {
  return (dispatch, getState) => {
    const { options, people } = getState().directory;
    repo.contents(options.people).fetch().then((res) => {

      const dataFromGitHub = JSON.parse(Base64.decode(res.content)).filter((d) => {
        return username.toLowerCase() !== d.github.toLowerCase();
      });

      const payload = JSON.stringify(dataFromGitHub, null, 2) + '\n';
      const putData = {
        message: 'Removed ' + username,
        content: Base64.encode(payload),
        sha: res.sha
      };

      repo.contents(options.people).add(putData)
        .then(() => {
          dispatch(setPeople(dataFromGitHub));
          cb(null);
        }).catch((err) => { cb(err); });
      }).catch((err) => { cb(err); });
  }
}

export function loadForm() {
  return (dispatch, getState) => {
    const { options } = getState().directory;
    repo.contents(options.form).read()
      .then((res) => {
        res = JSON.parse(res);
        let data = [];
        for (const prop in res) {
          data.push({
            section: prop,
            data: res[prop]
          });
        }
        dispatch(setForm(data));
      })
      .catch((err) => {
        console.log('whats the error?', err);
        dispatch(setForm([]));
      });
  };
}

export function loadUser(u) {
  return (dispatch, getState) => {
    const { people } = getState().directory;
    const user = people.filter((d) => {
      return u.toLowerCase() === d.github.toLowerCase();
    })[0];

    dispatch({
      type: types.USER,
      user
    });
  };
}

export function loadPeople(query) {
  const filter = (query && query.filter) ? query.filter : null;
  const sort = (query && query.sort) ? query.sort : 'name';

  return (dispatch, getState) => {
    const { options } = getState().directory;
    repo.contents(options.people).read()
      .then((data) => {
        data = JSON.parse(data);

        client.user.fetch()
          .then((user) => {

            data.forEach((d) => {
              if (d.github.toLowerCase() === user.login.toLowerCase() && d.admin) {
                user.admin = true;
              }
            });

            dispatch(setActor(user));
            dispatch(setPeople(data));
          });
      })
      .catch((err) => {
        console.log('An error occurred', err);
        dispatch(setPeople([]));
      });
  };
}

export function dismissError() {
  return (dispatch) => {
    dispatch(setError(''));
  };
}

export function dismissModal() {
  return (dispatch) => {
    dispatch(setMessage(''));
  };
}
<|endoftext|>


<commit_before>import * as ACTION_TYPES from '../constants/action_types'
import * as MAPPING_TYPES from '../constants/mapping_types'
import * as api from '../networking/api'
import * as StreamRenderables from '../components/streams/StreamRenderables'

const PLACEHOLDER_B64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYAQMAAACguBAzAAAAA1B'+
  'MVEXf398xY1lCAAAAUElEQVR4Xu3AAQEAAACCoP6vboiwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'+
  'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA47LgAARkTcJoAAAAASUVORK5CYII='

export function loadPostDetail(idOrToken) {
  return {
    type: ACTION_TYPES.POST.DETAIL,
    payload: {
      endpoint: api.postDetail(idOrToken),
      postIdOrToken: idOrToken,
    },
    meta: {
      mappingType: MAPPING_TYPES.POSTS,
      updateResult: false,
    },
  }
}

export function loadEditablePost(idOrToken) {
  return {
    type: ACTION_TYPES.POST.EDITABLE,
    payload: { endpoint: api.editPostDetail(idOrToken) },
    meta: {
      mappingType: MAPPING_TYPES.POSTS,
      updateResult: false,
    },
  }
}

export function loadComments(idOrToken, addUpdateKey = true) {
  const obj = {
    type: ACTION_TYPES.LOAD_STREAM,
    payload: {
      endpoint: api.commentsForPost(idOrToken),
      postIdOrToken: idOrToken.replace('~', ''),
    },
    meta: {
      mappingType: MAPPING_TYPES.COMMENTS,
      renderStream: {
        asList: StreamRenderables.commentsAsList,
        asGrid: StreamRenderables.commentsAsList,
      },
      resultKey: `/posts/${idOrToken}/comments`,
    },
  }
  if (addUpdateKey) {
    obj.meta.updateKey = `/posts/${idOrToken}/`
  }
  return obj
}

export function toggleComments(post, showComments) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_COMMENTS,
    payload: {
      model: post,
      showComments,
    },
  }
}

export function toggleEditing(post, isEditing) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_EDITING,
    payload: {
      model: post,
      isEditing,
    },
  }
}

export function toggleLovers(post, showLovers) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_LOVERS,
    payload: {
      model: post,
      showLovers,
    },
  }
}

export function toggleReposters(post, showReposters) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_REPOSTERS,
    payload: {
      model: post,
      showReposters,
    },
  }
}

export function toggleReposting(post, isReposting) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_REPOSTING,
    payload: {
      model: post,
      isReposting,
    },
  }
}

export function lovePost(post) {
  return {
    type: ACTION_TYPES.POST.LOVE,
    payload: {
      endpoint: api.lovePost(post.id),
      method: 'POST',
      model: post,
    },
    meta: {
      mappingType: MAPPING_TYPES.LOVES,
      resultKey: `/posts/${post.id}/love`,
      updateKey: `/posts/${post.id}/`,
    },
  }
}

export function unlovePost(post) {
  return {
    type: ACTION_TYPES.POST.LOVE,
    payload: {
      endpoint: api.unlovePost(post.id),
      method: 'DELETE',
      model: post,
    },
    meta: {
      resultKey: `/posts/${post.id}/love`,
      updateKey: `/posts/${post.id}/`,
    },
  }
}

export function deletePost(post) {
  return {
    type: ACTION_TYPES.POST.DELETE,
    payload: {
      endpoint: api.deletePost(post.id),
      method: 'DELETE',
      model: post,
    },
    meta: {},
  }
}

export function flagPost(post, kind) {
  return {
    type: ACTION_TYPES.POST.FLAG,
    payload: {
      endpoint: api.flagPost(post.id, kind),
      method: 'POST',
    },
    meta: {},
  }
}

export function createPost(body, editorId, repostId) {
  return {
    type: ACTION_TYPES.POST.CREATE,
    payload: {
      body: body.length ? { body } : null,
      editorId,
      endpoint: api.createPost(repostId),
      method: 'POST',
    },
    meta: {},
  }
}

export function createComment(body, editorId, postId) {
  return {
    type: ACTION_TYPES.COMMENT.CREATE,
    payload: {
      body: { body },
      editorId,
      endpoint: api.createComment(postId),
      method: 'POST',
      postId,
    },
    meta: {},
  }
}

export function updatePost(post, body) {
  return {
    type: ACTION_TYPES.POST.UPDATE,
    payload: {
      body: { body },
      endpoint: api.updatePost(post.id),
      method: 'PATCH',
      model: post,
    },
    meta: {},
  }
}

export function uploadAsset(type, file, editorId, index) {
  return {
    type,
    meta: {},
    payload: {
      editorId,
      file,
      index,
    },
  }
}

export function temporaryPostImageCreated(b64Asset, editorId, index) {
  return {
    type: ACTION_TYPES.POST.TMP_IMAGE_CREATED,
    meta: {},
    payload: {
      url: (b64Asset.length > 900000) ? PLACEHOLDER_B64 : b64Asset,
      editorId,
      index,
    },
  }
}

export function savePostImage(file, editorId, index) {
  return dispatch => {
    const reader = new FileReader()
    reader.onloadend = () => {
      dispatch(temporaryPostImageCreated(reader.result, editorId, index))
    }
    dispatch(uploadAsset(ACTION_TYPES.POST.SAVE_IMAGE, file, editorId, index))
    reader.readAsDataURL(file)
  }
}

export function postPreviews(embedUrl, editorId, index) {
  return {
    type: ACTION_TYPES.POST.POST_PREVIEW,
    payload: {
      body: { body: [{ kind: 'embed', data: { url: embedUrl } }] },
      editorId,
      endpoint: api.postPreviews(),
      index,
      method: 'POST',
    },
  }
}

export function autoCompleteUsers(type, word) {
  return {
    type: ACTION_TYPES.POST.AUTO_COMPLETE,
    payload: {
      endpoint: api.userAutocompleter(word),
      type,
    },
  }
}

export function loadEmojis(type, word) {
  return {
    type: ACTION_TYPES.EMOJI.LOAD,
    payload: {
      endpoint: api.loadEmojis(),
      type,
      word,
    },
  }
}

<commit_msg>whitespace/linting<commit_after>import * as ACTION_TYPES from '../constants/action_types'
import * as MAPPING_TYPES from '../constants/mapping_types'
import * as api from '../networking/api'
import * as StreamRenderables from '../components/streams/StreamRenderables'

const PLACEHOLDER_B64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYAQMAAACguBAzAAAAA1' +
  'BMVEXf398xY1lCAAAAUElEQVR4Xu3AAQEAAACCoP6vboiwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
  'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA47LgAARkTcJoAAAAASUVORK5CYII='

export function loadPostDetail(idOrToken) {
  return {
    type: ACTION_TYPES.POST.DETAIL,
    payload: {
      endpoint: api.postDetail(idOrToken),
      postIdOrToken: idOrToken,
    },
    meta: {
      mappingType: MAPPING_TYPES.POSTS,
      updateResult: false,
    },
  }
}

export function loadEditablePost(idOrToken) {
  return {
    type: ACTION_TYPES.POST.EDITABLE,
    payload: { endpoint: api.editPostDetail(idOrToken) },
    meta: {
      mappingType: MAPPING_TYPES.POSTS,
      updateResult: false,
    },
  }
}

export function loadComments(idOrToken, addUpdateKey = true) {
  const obj = {
    type: ACTION_TYPES.LOAD_STREAM,
    payload: {
      endpoint: api.commentsForPost(idOrToken),
      postIdOrToken: idOrToken.replace('~', ''),
    },
    meta: {
      mappingType: MAPPING_TYPES.COMMENTS,
      renderStream: {
        asList: StreamRenderables.commentsAsList,
        asGrid: StreamRenderables.commentsAsList,
      },
      resultKey: `/posts/${idOrToken}/comments`,
    },
  }
  if (addUpdateKey) {
    obj.meta.updateKey = `/posts/${idOrToken}/`
  }
  return obj
}

export function toggleComments(post, showComments) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_COMMENTS,
    payload: {
      model: post,
      showComments,
    },
  }
}

export function toggleEditing(post, isEditing) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_EDITING,
    payload: {
      model: post,
      isEditing,
    },
  }
}

export function toggleLovers(post, showLovers) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_LOVERS,
    payload: {
      model: post,
      showLovers,
    },
  }
}

export function toggleReposters(post, showReposters) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_REPOSTERS,
    payload: {
      model: post,
      showReposters,
    },
  }
}

export function toggleReposting(post, isReposting) {
  return {
    type: ACTION_TYPES.POST.TOGGLE_REPOSTING,
    payload: {
      model: post,
      isReposting,
    },
  }
}

export function lovePost(post) {
  return {
    type: ACTION_TYPES.POST.LOVE,
    payload: {
      endpoint: api.lovePost(post.id),
      method: 'POST',
      model: post,
    },
    meta: {
      mappingType: MAPPING_TYPES.LOVES,
      resultKey: `/posts/${post.id}/love`,
      updateKey: `/posts/${post.id}/`,
    },
  }
}

export function unlovePost(post) {
  return {
    type: ACTION_TYPES.POST.LOVE,
    payload: {
      endpoint: api.unlovePost(post.id),
      method: 'DELETE',
      model: post,
    },
    meta: {
      resultKey: `/posts/${post.id}/love`,
      updateKey: `/posts/${post.id}/`,
    },
  }
}

export function deletePost(post) {
  return {
    type: ACTION_TYPES.POST.DELETE,
    payload: {
      endpoint: api.deletePost(post.id),
      method: 'DELETE',
      model: post,
    },
    meta: {},
  }
}

export function flagPost(post, kind) {
  return {
    type: ACTION_TYPES.POST.FLAG,
    payload: {
      endpoint: api.flagPost(post.id, kind),
      method: 'POST',
    },
    meta: {},
  }
}

export function createPost(body, editorId, repostId) {
  return {
    type: ACTION_TYPES.POST.CREATE,
    payload: {
      body: body.length ? { body } : null,
      editorId,
      endpoint: api.createPost(repostId),
      method: 'POST',
    },
    meta: {},
  }
}

export function createComment(body, editorId, postId) {
  return {
    type: ACTION_TYPES.COMMENT.CREATE,
    payload: {
      body: { body },
      editorId,
      endpoint: api.createComment(postId),
      method: 'POST',
      postId,
    },
    meta: {},
  }
}

export function updatePost(post, body) {
  return {
    type: ACTION_TYPES.POST.UPDATE,
    payload: {
      body: { body },
      endpoint: api.updatePost(post.id),
      method: 'PATCH',
      model: post,
    },
    meta: {},
  }
}

export function uploadAsset(type, file, editorId, index) {
  return {
    type,
    meta: {},
    payload: {
      editorId,
      file,
      index,
    },
  }
}

export function temporaryPostImageCreated(b64Asset, editorId, index) {
  return {
    type: ACTION_TYPES.POST.TMP_IMAGE_CREATED,
    meta: {},
    payload: {
      url: (b64Asset.length > 900000) ? PLACEHOLDER_B64 : b64Asset,
      editorId,
      index,
    },
  }
}

export function savePostImage(file, editorId, index) {
  return dispatch => {
    const reader = new FileReader()
    reader.onloadend = () => {
      dispatch(temporaryPostImageCreated(reader.result, editorId, index))
    }
    dispatch(uploadAsset(ACTION_TYPES.POST.SAVE_IMAGE, file, editorId, index))
    reader.readAsDataURL(file)
  }
}

export function postPreviews(embedUrl, editorId, index) {
  return {
    type: ACTION_TYPES.POST.POST_PREVIEW,
    payload: {
      body: { body: [{ kind: 'embed', data: { url: embedUrl } }] },
      editorId,
      endpoint: api.postPreviews(),
      index,
      method: 'POST',
    },
  }
}

export function autoCompleteUsers(type, word) {
  return {
    type: ACTION_TYPES.POST.AUTO_COMPLETE,
    payload: {
      endpoint: api.userAutocompleter(word),
      type,
    },
  }
}

export function loadEmojis(type, word) {
  return {
    type: ACTION_TYPES.EMOJI.LOAD,
    payload: {
      endpoint: api.loadEmojis(),
      type,
      word,
    },
  }
}

<|endoftext|>


<commit_before>import Dispatcher from '../dispatcher/Dispatcher';
import Store from '../store/Store';
import types from '../constants/actionTypes';
import modes from '../constants/modes';
import { STROKE_SIZE, STROKE_COLOR } from '../constants/graphical';

export default function press(x, y) {
  switch (Store.getMode()) {

    case modes.DRAW:
      Dispatcher.dispatch({
        type: types.ACTIVITY_UPDATE,
        inProgress: true,
      });
      Dispatcher.dispatch({
        type: types.ANNOTATIONS_CREATE,
        annotation: [
          'path', {
            fill: 'none',
            d: `M${x} ${y}`,
            stroke: STROKE_COLOR,
            'stroke-width': STROKE_SIZE,
            'stronke-height': STROKE_SIZE,
            'stroke-linejoin': 'round',
            'stroke-linecap': 'round',
            'vector-effect': 'non-scaling-stroke',
          },
        ],
      });
      break;

  }
}
<commit_msg>Fix typo<commit_after>import Dispatcher from '../dispatcher/Dispatcher';
import Store from '../store/Store';
import types from '../constants/actionTypes';
import modes from '../constants/modes';
import { STROKE_SIZE, STROKE_COLOR } from '../constants/graphical';

export default function press(x, y) {
  switch (Store.getMode()) {

    case modes.DRAW:
      Dispatcher.dispatch({
        type: types.ACTIVITY_UPDATE,
        inProgress: true,
      });
      Dispatcher.dispatch({
        type: types.ANNOTATIONS_CREATE,
        annotation: [
          'path', {
            fill: 'none',
            d: `M${x} ${y}`,
            stroke: STROKE_COLOR,
            'stroke-width': STROKE_SIZE,
            'stroke-height': STROKE_SIZE,
            'stroke-linejoin': 'round',
            'stroke-linecap': 'round',
            'vector-effect': 'non-scaling-stroke',
          },
        ],
      });
      break;

  }
}
<|endoftext|>


<commit_before>const {Transform} = require("prosemirror-transform")
const {Mark} = require("prosemirror-model")
const {Selection} = require("./selection")

const UPDATED_SEL = 1, UPDATED_MARKS = 2, UPDATED_SCROLL = 4

// ::- An editor state transaction, which can be applied to a state to
// create an updated state. Use
// [`EditorState.tr`](#state.EditorState.tr) to create an instance.
//
// Transactions track changes to the document (they are a subclass of
// [`Transform`](#transform.Transform)), but also other state changes,
// like selection updates and adjustments of the set of [stored
// marks](##state.EditorState.storedMarks). In addition, you can store
// metadata properties in a transaction, which are extra pieces of
// informations that client code or plugins can use to describe what a
// transacion represents, so that they can update their [own
// state](##state.StateField) accordingly.
//
// The [editor view](##view.EditorView) uses a single metadata
// property: it will attach a property `"pointer"` with the value
// `true` to selection transactions directly caused by mouse or touch
// input.
class Transaction extends Transform {
  constructor(state) {
    super(state.doc)
    // :: number
    // The timestamp associated with this transaction.
    this.time = Date.now()
    this.curSelection = state.selection
    // The step count for which the current selection is valid.
    this.curSelectionFor = 0
    // :: ?[Mark]
    // The stored marks in this transaction.
    this.storedMarks = state.storedMarks
    // Bitfield to track which aspects of the state were updated by
    // this transaction.
    this.updated = 0
    // Object used to store metadata properties for the transaction.
    this.meta = Object.create(null)
  }

  // :: bool
  // True when this transaction changes the document.
  get docChanged() {
    return this.steps.length > 0
  }

  // :: Selection
  // The transform's current selection. This defaults to the
  // editor selection [mapped](#state.Selection.map) through the steps in
  // this transform, but can be overwritten with
  // [`setSelection`](#state.Transaction.setSelection).
  get selection() {
    if (this.curSelectionFor < this.steps.length) {
      this.curSelection = this.curSelection.map(this.doc, this.mapping.slice(this.curSelectionFor))
      this.curSelectionFor = this.steps.length
    }
    return this.curSelection
  }

  // :: (Selection) → Transaction
  // Update the transaction's current selection. This will determine
  // the selection that the editor gets when the transaction is
  // applied.
  setSelection(selection) {
    this.curSelection = selection
    this.curSelectionFor = this.steps.length
    this.updated = (this.updated | UPDATED_SEL) & ~UPDATED_MARKS
    this.storedMarks = null
    return this
  }

  // :: bool
  // Whether the selection was explicitly updated by this transaction.
  get selectionSet() {
    return (this.updated & UPDATED_SEL) > 0
  }

  // :: (?[Mark]) → Transaction
  // Replace the set of stored marks.
  setStoredMarks(marks) {
    this.storedMarks = marks
    this.updated |= UPDATED_MARKS
    return this
  }

  // :: bool
  // Whether the stored marks were explicitly set for this transaction.
  get storedMarksSet() {
    return (this.updated & UPDATED_MARKS) > 0
  }

  addStep(step, doc) {
    super.addStep(step, doc)
    this.updated = this.updated & ~UPDATED_MARKS
    this.storedMarks = null
  }

  // :: (number) → Transaction
  // Update the timestamp for the transaction.
  setTime(time) {
    this.time = time
    return this
  }

  // :: (Slice) → Transaction
  replaceSelection(slice) {
    let {from, to} = this.selection, startLen = this.steps.length
    this.replaceRange(from, to, slice)
    // Move the selection to the position after the inserted content.
    // When that ended in an inline node, search backwards, to get the
    // position after that node. If not, search forward.
    let lastNode = slice.content.lastChild, lastParent = null
    for (let i = 0; i < slice.openRight; i++) {
      lastParent = lastNode
      lastNode = lastNode.lastChild
    }
    selectionToInsertionEnd(this, startLen, (lastNode ? lastNode.isInline : lastParent && lastParent.isTextblock) ? -1 : 1)
    return this
  }

  // :: (Node, ?bool) → Transaction
  // Replace the selection with the given node or slice, or delete it
  // if `content` is null. When `inheritMarks` is true and the content
  // is inline, it inherits the marks from the place where it is
  // inserted.
  replaceSelectionWith(node, inheritMarks) {
    let {$from, from, to} = this.selection, startLen = this.steps.length
    if (inheritMarks !== false)
      node = node.mark(this.storedMarks || $from.marks(to > from))
    this.replaceRangeWith(from, to, node)
    selectionToInsertionEnd(this, startLen, node.isInline ? -1 : 1)
    return this
  }

  // :: () → Transaction
  // Delete the selection.
  deleteSelection() {
    let {from, to} = this.selection
    return this.deleteRange(from, to)
  }

  // :: (string, from: ?number, to: ?number) → Transaction
  // Replace the given range, or the selection if no range is given,
  // with a text node containing the given string.
  insertText(text, from, to = from) {
    let schema = this.doc.type.schema
    if (from == null) {
      if (!text) return this.deleteSelection()
      return this.replaceSelectionWith(schema.text(text), true)
    } else {
      if (!text) return this.deleteRange(from, to)
      let node = schema.text(text, this.storedMarks || this.doc.resolve(from).marks(to > from))
      return this.replaceRangeWith(from, to, node)
    }
  }

  // :: (union<string, Plugin, PluginKey>, any) → Transaction
  // Store a metadata property in this transaction, keyed either by
  // name or by plugin.
  setMeta(key, value) {
    this.meta[typeof key == "string" ? key : key.key] = value
    return this
  }

  // :: (union<string, Plugin, PluginKey>) → any
  // Retrieve a metadata property for a given name or plugin.
  getMeta(key) {
    return this.meta[typeof key == "string" ? key : key.key]
  }

  // :: bool
  // Returns true if this transaction doesn't contain any metadata,
  // and can thus be safely extended.
  get isGeneric() {
    for (let _ in this.meta) return false
    return true
  }

  // :: () → Transaction
  // Indicate that the editor should scroll the selection into view
  // when updated to the state produced by this transaction.
  scrollIntoView() {
    this.updated |= UPDATED_SCROLL
    return this
  }

  get scrolledIntoView() {
    return (this.updated & UPDATED_SCROLL) > 0
  }

  // :: (Mark) → Transaction
  // Add a mark to the set of stored marks.
  addStoredMark(mark) {
    this.storedMarks = mark.addToSet(this.storedMarks || currentMarks(this.selection))
    return this
  }

  // :: (union<Mark, MarkType>) → Transaction
  // Remove a mark or mark type from the set of stored marks.
  removeStoredMark(mark) {
    this.storedMarks = mark.removeFromSet(this.storedMarks || currentMarks(this.selection))
    return this
  }
}
exports.Transaction = Transaction

function selectionToInsertionEnd(tr, startLen, bias) {
  if (tr.steps.length == startLen) return
  let map = tr.mapping.maps[tr.mapping.maps.length - 1], end
  map.forEach((_from, _to, _newFrom, newTo) => end = newTo)
  if (end != null) tr.setSelection(Selection.near(tr.doc.resolve(end), bias))
}

function currentMarks(selection) {
  return selection.head == null ? Mark.none : selection.$head.marks()
}
<commit_msg>Move docChanged to Transform<commit_after>const {Transform} = require("prosemirror-transform")
const {Mark} = require("prosemirror-model")
const {Selection} = require("./selection")

const UPDATED_SEL = 1, UPDATED_MARKS = 2, UPDATED_SCROLL = 4

// ::- An editor state transaction, which can be applied to a state to
// create an updated state. Use
// [`EditorState.tr`](#state.EditorState.tr) to create an instance.
//
// Transactions track changes to the document (they are a subclass of
// [`Transform`](#transform.Transform)), but also other state changes,
// like selection updates and adjustments of the set of [stored
// marks](##state.EditorState.storedMarks). In addition, you can store
// metadata properties in a transaction, which are extra pieces of
// informations that client code or plugins can use to describe what a
// transacion represents, so that they can update their [own
// state](##state.StateField) accordingly.
//
// The [editor view](##view.EditorView) uses a single metadata
// property: it will attach a property `"pointer"` with the value
// `true` to selection transactions directly caused by mouse or touch
// input.
class Transaction extends Transform {
  constructor(state) {
    super(state.doc)
    // :: number
    // The timestamp associated with this transaction.
    this.time = Date.now()
    this.curSelection = state.selection
    // The step count for which the current selection is valid.
    this.curSelectionFor = 0
    // :: ?[Mark]
    // The stored marks in this transaction.
    this.storedMarks = state.storedMarks
    // Bitfield to track which aspects of the state were updated by
    // this transaction.
    this.updated = 0
    // Object used to store metadata properties for the transaction.
    this.meta = Object.create(null)
  }

  // :: Selection
  // The transform's current selection. This defaults to the
  // editor selection [mapped](#state.Selection.map) through the steps in
  // this transform, but can be overwritten with
  // [`setSelection`](#state.Transaction.setSelection).
  get selection() {
    if (this.curSelectionFor < this.steps.length) {
      this.curSelection = this.curSelection.map(this.doc, this.mapping.slice(this.curSelectionFor))
      this.curSelectionFor = this.steps.length
    }
    return this.curSelection
  }

  // :: (Selection) → Transaction
  // Update the transaction's current selection. This will determine
  // the selection that the editor gets when the transaction is
  // applied.
  setSelection(selection) {
    this.curSelection = selection
    this.curSelectionFor = this.steps.length
    this.updated = (this.updated | UPDATED_SEL) & ~UPDATED_MARKS
    this.storedMarks = null
    return this
  }

  // :: bool
  // Whether the selection was explicitly updated by this transaction.
  get selectionSet() {
    return (this.updated & UPDATED_SEL) > 0
  }

  // :: (?[Mark]) → Transaction
  // Replace the set of stored marks.
  setStoredMarks(marks) {
    this.storedMarks = marks
    this.updated |= UPDATED_MARKS
    return this
  }

  // :: bool
  // Whether the stored marks were explicitly set for this transaction.
  get storedMarksSet() {
    return (this.updated & UPDATED_MARKS) > 0
  }

  addStep(step, doc) {
    super.addStep(step, doc)
    this.updated = this.updated & ~UPDATED_MARKS
    this.storedMarks = null
  }

  // :: (number) → Transaction
  // Update the timestamp for the transaction.
  setTime(time) {
    this.time = time
    return this
  }

  // :: (Slice) → Transaction
  replaceSelection(slice) {
    let {from, to} = this.selection, startLen = this.steps.length
    this.replaceRange(from, to, slice)
    // Move the selection to the position after the inserted content.
    // When that ended in an inline node, search backwards, to get the
    // position after that node. If not, search forward.
    let lastNode = slice.content.lastChild, lastParent = null
    for (let i = 0; i < slice.openRight; i++) {
      lastParent = lastNode
      lastNode = lastNode.lastChild
    }
    selectionToInsertionEnd(this, startLen, (lastNode ? lastNode.isInline : lastParent && lastParent.isTextblock) ? -1 : 1)
    return this
  }

  // :: (Node, ?bool) → Transaction
  // Replace the selection with the given node or slice, or delete it
  // if `content` is null. When `inheritMarks` is true and the content
  // is inline, it inherits the marks from the place where it is
  // inserted.
  replaceSelectionWith(node, inheritMarks) {
    let {$from, from, to} = this.selection, startLen = this.steps.length
    if (inheritMarks !== false)
      node = node.mark(this.storedMarks || $from.marks(to > from))
    this.replaceRangeWith(from, to, node)
    selectionToInsertionEnd(this, startLen, node.isInline ? -1 : 1)
    return this
  }

  // :: () → Transaction
  // Delete the selection.
  deleteSelection() {
    let {from, to} = this.selection
    return this.deleteRange(from, to)
  }

  // :: (string, from: ?number, to: ?number) → Transaction
  // Replace the given range, or the selection if no range is given,
  // with a text node containing the given string.
  insertText(text, from, to = from) {
    let schema = this.doc.type.schema
    if (from == null) {
      if (!text) return this.deleteSelection()
      return this.replaceSelectionWith(schema.text(text), true)
    } else {
      if (!text) return this.deleteRange(from, to)
      let node = schema.text(text, this.storedMarks || this.doc.resolve(from).marks(to > from))
      return this.replaceRangeWith(from, to, node)
    }
  }

  // :: (union<string, Plugin, PluginKey>, any) → Transaction
  // Store a metadata property in this transaction, keyed either by
  // name or by plugin.
  setMeta(key, value) {
    this.meta[typeof key == "string" ? key : key.key] = value
    return this
  }

  // :: (union<string, Plugin, PluginKey>) → any
  // Retrieve a metadata property for a given name or plugin.
  getMeta(key) {
    return this.meta[typeof key == "string" ? key : key.key]
  }

  // :: bool
  // Returns true if this transaction doesn't contain any metadata,
  // and can thus be safely extended.
  get isGeneric() {
    for (let _ in this.meta) return false
    return true
  }

  // :: () → Transaction
  // Indicate that the editor should scroll the selection into view
  // when updated to the state produced by this transaction.
  scrollIntoView() {
    this.updated |= UPDATED_SCROLL
    return this
  }

  get scrolledIntoView() {
    return (this.updated & UPDATED_SCROLL) > 0
  }

  // :: (Mark) → Transaction
  // Add a mark to the set of stored marks.
  addStoredMark(mark) {
    this.storedMarks = mark.addToSet(this.storedMarks || currentMarks(this.selection))
    return this
  }

  // :: (union<Mark, MarkType>) → Transaction
  // Remove a mark or mark type from the set of stored marks.
  removeStoredMark(mark) {
    this.storedMarks = mark.removeFromSet(this.storedMarks || currentMarks(this.selection))
    return this
  }
}
exports.Transaction = Transaction

function selectionToInsertionEnd(tr, startLen, bias) {
  if (tr.steps.length == startLen) return
  let map = tr.mapping.maps[tr.mapping.maps.length - 1], end
  map.forEach((_from, _to, _newFrom, newTo) => end = newTo)
  if (end != null) tr.setSelection(Selection.near(tr.doc.resolve(end), bias))
}

function currentMarks(selection) {
  return selection.head == null ? Mark.none : selection.$head.marks()
}
<|endoftext|>


<commit_before>window.ActivitiesView = Backbone.Marionette.CollectionView.extend({
  initialize: function(opts) {
    this.itemView = ActivityView;
  },
});
<commit_msg>@markijbema can't type<commit_after>window.ActivitiesView = Backbone.Marionette.CollectionView.extend({
  initialize: function(opts) {
    this.itemView = ActivityView;
  }
});
<|endoftext|>


<commit_before>import t from 'tcomb'
import * as m from './multimedia-types'
import * as k from './keyboard-types'

export const User = t.struct({
  id: t.Number,
  first_name: t.String,
  last_name: t.maybe(t.String),
  username: t.maybe(t.String)
})

export const Chat = t.struct({
  id: t.Number,
  type: t.String,
  title: t.maybe(t.String),
  username: t.maybe(t.String),
  first_name: t.maybe(t.String),
  last_name: t.maybe(t.String)
})

export const InputMessageContent = t.struct({
  message_text: t.String,
  parse_mode: t.maybe(t.String),
  disable_web_page_preview: t.maybe(t.Boolean)
})

export const InlineQueryResult = t.struct({

})

export const InlineQueryResultArticle = InlineQueryResult.extend({
  type: t.String,
  id: t.String,
  title: t.String,
  input_message_content: InputMessageContent,
  reply_markup: t.maybe(k.InlineKeyboardMarkup),
  url: t.maybe(t.String),
  hide_url: t.maybe(t.Boolean),
  description: t.maybe(t.String),
  thumb_url: t.maybe(t.String),
  thumb_width: t.maybe(t.Number),
  thumb_height: t.maybe(t.Number)
})

export const MessageEntity = t.struct({
  type: t.enums.of(['mention', 'hashtag', 'bot_command', 'url', 'email', 'bold', 'italic', 'code', 'pre', 'text_link']),
  offset: t.Number,
  length: t.Number,
  url: t.maybe(t.String)
})

export const Message = t.declare('Message')

Message.define(t.struct({
  message_id: t.Number,
  from: t.maybe(User),
  date: t.Number,
  chat: Chat,
  forward_from: t.maybe(User),
  forward_date: t.maybe(t.Number),
  reply_to_message: t.maybe(Message),
  text: t.maybe(t.String),
  entities: t.maybe(t.list(MessageEntity)),
  audio: t.maybe(m.Audio),
  document: t.maybe(m.Document),
  photo: t.maybe(t.list(m.PhotoSize)),
  sticker: t.maybe(m.Sticker),
  video: t.maybe(m.Video),
  voice: t.maybe(m.Voice),
  caption: t.maybe(t.String),
  contact: t.maybe(m.Contact),
  location: t.maybe(m.Location),
  venue: t.maybe(m.Venue),
  new_chat_member: t.maybe(User),
  left_chat_member: t.maybe(User),
  new_chat_title: t.maybe(t.String),
  new_chat_photo: t.maybe(t.list(m.PhotoSize)),
  delete_chat_photo: t.maybe(t.Boolean),
  group_chat_created: t.maybe(t.Boolean),
  supergroup_chat_created: t.maybe(t.Boolean),
  channel_chat_created: t.maybe(t.Boolean),
  migrate_to_chat_id: t.maybe(t.Number),
  migrate_from_chat_id: t.maybe(t.Number),
  pinned_message: t.maybe(Message)
}))

export const InlineQuery = t.struct({
  id: t.String,
  from: User,
  location: t.maybe(m.Location),
  query: t.String,
  offset: t.String
})

export const ChosenInlineResult = t.struct({
  result_id: t.String,
  from: User,
  location: t.maybe(m.Location),
  inline_message_id: t.maybe(t.String),
  query: t.String
})

export const CallbackQuery = t.struct({
  id: t.String,
  from: User,
  message: t.maybe(Message),
  inline_message_id: t.maybe(t.String),
  data: t.maybe(t.String)
})

export const Update = t.struct({
  update_id: t.Number,
  message: t.maybe(Message),
  inline_query: t.maybe(InlineQuery),
  chosen_inline_result: t.maybe(ChosenInlineResult),
  callback_query: t.maybe(CallbackQuery)
})

export const UpdatesState = t.struct({
  startDate: t.Number,
  offset: t.Number,
  updates: t.list(Update)
})

export const Request = t.struct({
  type: t.enums.of(['sink']),
  method: t.String,
  options: t.Object
})
<commit_msg>add webhook type<commit_after>import t from 'tcomb'
import * as m from './multimedia-types'
import * as k from './keyboard-types'

export const User = t.struct({
  id: t.Number,
  first_name: t.String,
  last_name: t.maybe(t.String),
  username: t.maybe(t.String)
})

export const Chat = t.struct({
  id: t.Number,
  type: t.String,
  title: t.maybe(t.String),
  username: t.maybe(t.String),
  first_name: t.maybe(t.String),
  last_name: t.maybe(t.String)
})

export const InputMessageContent = t.struct({
  message_text: t.String,
  parse_mode: t.maybe(t.String),
  disable_web_page_preview: t.maybe(t.Boolean)
})

export const InlineQueryResult = t.struct({

})

export const InlineQueryResultArticle = InlineQueryResult.extend({
  type: t.String,
  id: t.String,
  title: t.String,
  input_message_content: InputMessageContent,
  reply_markup: t.maybe(k.InlineKeyboardMarkup),
  url: t.maybe(t.String),
  hide_url: t.maybe(t.Boolean),
  description: t.maybe(t.String),
  thumb_url: t.maybe(t.String),
  thumb_width: t.maybe(t.Number),
  thumb_height: t.maybe(t.Number)
})

export const MessageEntity = t.struct({
  type: t.enums.of(['mention', 'hashtag', 'bot_command', 'url', 'email', 'bold', 'italic', 'code', 'pre', 'text_link']),
  offset: t.Number,
  length: t.Number,
  url: t.maybe(t.String)
})

export const Message = t.declare('Message')

Message.define(t.struct({
  message_id: t.Number,
  from: t.maybe(User),
  date: t.Number,
  chat: Chat,
  forward_from: t.maybe(User),
  forward_date: t.maybe(t.Number),
  reply_to_message: t.maybe(Message),
  text: t.maybe(t.String),
  entities: t.maybe(t.list(MessageEntity)),
  audio: t.maybe(m.Audio),
  document: t.maybe(m.Document),
  photo: t.maybe(t.list(m.PhotoSize)),
  sticker: t.maybe(m.Sticker),
  video: t.maybe(m.Video),
  voice: t.maybe(m.Voice),
  caption: t.maybe(t.String),
  contact: t.maybe(m.Contact),
  location: t.maybe(m.Location),
  venue: t.maybe(m.Venue),
  new_chat_member: t.maybe(User),
  left_chat_member: t.maybe(User),
  new_chat_title: t.maybe(t.String),
  new_chat_photo: t.maybe(t.list(m.PhotoSize)),
  delete_chat_photo: t.maybe(t.Boolean),
  group_chat_created: t.maybe(t.Boolean),
  supergroup_chat_created: t.maybe(t.Boolean),
  channel_chat_created: t.maybe(t.Boolean),
  migrate_to_chat_id: t.maybe(t.Number),
  migrate_from_chat_id: t.maybe(t.Number),
  pinned_message: t.maybe(Message)
}))

export const InlineQuery = t.struct({
  id: t.String,
  from: User,
  location: t.maybe(m.Location),
  query: t.String,
  offset: t.String
})

export const ChosenInlineResult = t.struct({
  result_id: t.String,
  from: User,
  location: t.maybe(m.Location),
  inline_message_id: t.maybe(t.String),
  query: t.String
})

export const CallbackQuery = t.struct({
  id: t.String,
  from: User,
  message: t.maybe(Message),
  inline_message_id: t.maybe(t.String),
  data: t.maybe(t.String)
})

export const Update = t.struct({
  update_id: t.Number,
  message: t.maybe(Message),
  inline_query: t.maybe(InlineQuery),
  chosen_inline_result: t.maybe(ChosenInlineResult),
  callback_query: t.maybe(CallbackQuery)
})

export const UpdatesState = t.struct({
  startDate: t.Number,
  offset: t.Number,
  updates: t.list(Update)
})

export const Request = t.struct({
  type: t.enums.of(['sink']),
  method: t.String,
  options: t.Object
})

export const WebhookResponse = t.struct({
  type: t.enums.of(['webhook']),
  update: Update
})
<|endoftext|>


<commit_before>const jwt = require('jsonwebtoken');
const models = require('../../../models');
const common = require('../../../lib/common');

const JWT_OPTIONS = {
    maxAge: '5m',
    algorithms: ['HS256']
};

/**
 * Remove 'Ghost' from raw authorization header and extract the JWT token.
 * Eg. Authorization: Ghost ${JWT}
 * @param {string} header
 */
const _extractTokenFromHeader = function extractTokenFromHeader(header) {
    const [scheme, token] = header.split(' ');

    if (/^Ghost$/i.test(scheme)) {
        return token;
    }
};

/**
 * Admin API key authentication flow:
 * 1. extract the JWT token from the `Authorization: Ghost xxxx` header
 * 2. decode the JWT to extract the api_key id from the "key id" header claim
 * 3. find a matching api_key record
 * 4. verify the JWT (matching secret, matching URL path, not expired)
 * 5. place the api_key object on `req.api_key`
 *
 * There are some specifcs of the JWT that we expect:
 * - the "Key ID" header parameter should be set to the id of the api_key used to sign the token
 *   https://tools.ietf.org/html/rfc7515#section-4.1.4
 * - the "Audience" claim should match the requested API path
 *   https://tools.ietf.org/html/rfc7519#section-4.1.3
 */
const authenticate = (req, res, next) => {
    // CASE: we don't have an Authorization header so allow fallthrough to other
    // auth middleware or final "ensure authenticated" check
    if (!req.headers || !req.headers.authorization) {
        req.api_key = null;
        return next();
    }

    const token = _extractTokenFromHeader(req.headers.authorization);

    if (!token) {
        return next(new common.errors.UnauthorizedError({
            message: common.i18n.t('errors.middleware.auth.incorrectAuthHeaderFormat'),
            code: 'INVALID_AUTH_HEADER'
        }));
    }

    const decoded = jwt.decode(token, {complete: true});

    if (!decoded || !decoded.header) {
        return next(new common.errors.BadRequestError({
            message: common.i18n.t('errors.middleware.auth.invalidToken'),
            code: 'INVALID_JWT'
        }));
    }

    const apiKeyId = decoded.header.kid;

    if (!apiKeyId) {
        return next(new common.errors.BadRequestError({
            message: common.i18n.t('errors.middleware.auth.adminApiKidMissing'),
            code: 'MISSING_ADMIN_API_KID'
        }));
    }

    models.ApiKey.findOne({id: apiKeyId}).then((apiKey) => {
        if (!apiKey) {
            return next(new common.errors.UnauthorizedError({
                message: common.i18n.t('errors.middleware.auth.unknownAdminApiKey'),
                code: 'UNKNOWN_ADMIN_API_KEY'
            }));
        }

        if (apiKey.get('type') !== 'admin') {
            return next(new common.errors.UnauthorizedError({
                message: common.i18n.t('errors.middleware.auth.invalidApiKeyType'),
                code: 'INVALID_API_KEY_TYPE'
            }));
        }

        // Decoding from hex and transforming into bytes is here to
        // keep comparison of the bytes that are stored in the secret.
        // Useful context:
        // https://github.com/auth0/node-jsonwebtoken/issues/208#issuecomment-231861138
        const secret = Buffer.from(apiKey.get('secret'), 'hex');

        // @TODO When v3 api hits we should check against the api actually being used
        // ensure the token was meant for this api
        const options = Object.assign({
            audience: '/v2/admin/'
        }, JWT_OPTIONS);

        try {
            jwt.verify(token, secret, options);
        } catch (err) {
            if (err.name === 'TokenExpiredError' || err.name === 'JsonWebTokenError') {
                return next(new common.errors.UnauthorizedError({
                    message: common.i18n.t('errors.middleware.auth.invalidTokenWithMessage', {message: err.message}),
                    code: 'INVALID_JWT',
                    err
                }));
            }

            // unknown error
            return next(new common.errors.InternalServerError({err}));
        }

        // authenticated OK, store the api key on the request for later checks and logging
        req.api_key = apiKey;
        next();
    }).catch((err) => {
        next(new common.errors.InternalServerError({err}));
    });
};

module.exports = {
    authenticate
};
<commit_msg>Updated token verification to use dynamic audience check<commit_after>const jwt = require('jsonwebtoken');
const url = require('url');
const models = require('../../../models');
const common = require('../../../lib/common');

const JWT_OPTIONS = {
    maxAge: '5m',
    algorithms: ['HS256']
};

/**
 * Remove 'Ghost' from raw authorization header and extract the JWT token.
 * Eg. Authorization: Ghost ${JWT}
 * @param {string} header
 */
const _extractTokenFromHeader = function extractTokenFromHeader(header) {
    const [scheme, token] = header.split(' ');

    if (/^Ghost$/i.test(scheme)) {
        return token;
    }
};

/**
 * Admin API key authentication flow:
 * 1. extract the JWT token from the `Authorization: Ghost xxxx` header
 * 2. decode the JWT to extract the api_key id from the "key id" header claim
 * 3. find a matching api_key record
 * 4. verify the JWT (matching secret, matching URL path, not expired)
 * 5. place the api_key object on `req.api_key`
 *
 * There are some specifcs of the JWT that we expect:
 * - the "Key ID" header parameter should be set to the id of the api_key used to sign the token
 *   https://tools.ietf.org/html/rfc7515#section-4.1.4
 * - the "Audience" claim should match the requested API path
 *   https://tools.ietf.org/html/rfc7519#section-4.1.3
 */
const authenticate = (req, res, next) => {
    // CASE: we don't have an Authorization header so allow fallthrough to other
    // auth middleware or final "ensure authenticated" check
    if (!req.headers || !req.headers.authorization) {
        req.api_key = null;
        return next();
    }

    const token = _extractTokenFromHeader(req.headers.authorization);

    if (!token) {
        return next(new common.errors.UnauthorizedError({
            message: common.i18n.t('errors.middleware.auth.incorrectAuthHeaderFormat'),
            code: 'INVALID_AUTH_HEADER'
        }));
    }

    const decoded = jwt.decode(token, {complete: true});

    if (!decoded || !decoded.header) {
        return next(new common.errors.BadRequestError({
            message: common.i18n.t('errors.middleware.auth.invalidToken'),
            code: 'INVALID_JWT'
        }));
    }

    const apiKeyId = decoded.header.kid;

    if (!apiKeyId) {
        return next(new common.errors.BadRequestError({
            message: common.i18n.t('errors.middleware.auth.adminApiKidMissing'),
            code: 'MISSING_ADMIN_API_KID'
        }));
    }

    models.ApiKey.findOne({id: apiKeyId}).then((apiKey) => {
        if (!apiKey) {
            return next(new common.errors.UnauthorizedError({
                message: common.i18n.t('errors.middleware.auth.unknownAdminApiKey'),
                code: 'UNKNOWN_ADMIN_API_KEY'
            }));
        }

        if (apiKey.get('type') !== 'admin') {
            return next(new common.errors.UnauthorizedError({
                message: common.i18n.t('errors.middleware.auth.invalidApiKeyType'),
                code: 'INVALID_API_KEY_TYPE'
            }));
        }

        // Decoding from hex and transforming into bytes is here to
        // keep comparison of the bytes that are stored in the secret.
        // Useful context:
        // https://github.com/auth0/node-jsonwebtoken/issues/208#issuecomment-231861138
        const secret = Buffer.from(apiKey.get('secret'), 'hex');

        const {pathname} = url.parse(req.originalUrl);
        const [hasMatch, version = 'v2', api = 'admin'] = pathname.match(/ghost\/api\/([^/]+)\/([^/]+)\/(.+)*/); // eslint-disable-line no-unused-vars

        // ensure the token was meant for this api version
        const options = Object.assign({
            audience: new RegExp(`\/?${version}\/${api}\/?$`) // eslint-disable-line no-useless-escape
        }, JWT_OPTIONS);

        try {
            jwt.verify(token, secret, options);
        } catch (err) {
            if (err.name === 'TokenExpiredError' || err.name === 'JsonWebTokenError') {
                return next(new common.errors.UnauthorizedError({
                    message: common.i18n.t('errors.middleware.auth.invalidTokenWithMessage', {message: err.message}),
                    code: 'INVALID_JWT',
                    err
                }));
            }

            // unknown error
            return next(new common.errors.InternalServerError({err}));
        }

        // authenticated OK, store the api key on the request for later checks and logging
        req.api_key = apiKey;
        next();
    }).catch((err) => {
        next(new common.errors.InternalServerError({err}));
    });
};

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


<commit_before>(function(window, angular, undefined) {
'use strict';

var noop   = angular.noop,
  forEach  = angular.forEach,
  extend   = angular.extend,
  copy     = angular.copy,
  isFunc   = angular.isFunction,
  isObject = angular.isObject,
  isArray  = angular.isArray,
  isUndef  = angular.isUndefined;

function deepExtend(dst, source) {
  for (var prop in source) {
    if (source[prop] && source[prop].constructor && source[prop].constructor === Object) {
      dst[prop] = dst[prop] || {};
      deepExtend(dst[prop], source[prop]);
      continue;
    }
    dst[prop] = source[prop];
  }
  return dst;
}

function isEmpty(obj) {
  var name;
  for (name in obj) return false;
  return true;
}

function inherit(parent, extra) {
  return extend(new (extend(function() {}, {prototype: parent}))(), extra);
}

function hyphenate(str) {
  var toLower = function($1) { return "-" + $1.toLowerCase(); };
  return str.replace(/([A-Z])/g, toLower).replace(/^-+/, '');
}

function serialize(obj, prefix) {
  var str = [];
  var enc = encodeURIComponent;

  for (var p in obj) {
    var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
    str.push(isObject(v) ? serialize(v, k) : enc(k) + "=" + enc(v));
  }
  return str.join("&");
}

angular.module('ur.model', []).provider('model', function() {

  var expr, // Used to evaluate expressions; initialized by the service invokation
      http, // Provider-level copy of $http service
      q;    // Provider-level copy of $q service

  // "Box" an object value in a model instance if it is present.
  // Otherwise, return an empty model object.
  function autoBox(object, model, data) {
    if (!object) {
      return isArray(data) ? model.collection(data, true) : model.instance(data);
    }
    if (isArray(data) && isArray(object)) {
      return model.collection(data, true);
    }
    if (data && JSON.stringify(data).length > 3) {
      return extend(object, data);
    }
    return object;
  }

  // Global registry of settings
  var global = {

    // Base URL prepended to generated URLs
    base: "",

    // Extract URL from object, or use default URL
    url: function(object) {
      if (object instanceof ModelClass) {
        return object.url();
      }
      if (object instanceof ModelInstance || isFunc(object.$model)) {
        var model = object.$model();
        return expr(object, model.$config().identity).get() || model.url();
      }
      throw new Error("Could not get URL for " + typeof object);
    }
  };

  // Master registry of application model configurations
  var registry = {};

  // Default configuration for new model classes
  var DEFAULTS = {

    // Default values which should be applied when creating a new model instance
    defaults: {},

    // The name of the key to the URL that identifies the object
    identity: "$links.self",

    // The name of the key to assign an object map of errors
    errors: "$errors"
  };

  var DEFAULT_METHODS = {

    // Methods available on the model class
    $class: {
      all: function(data, headers) {
        return $request(null, this, 'GET', data, headers);
      },
      first: function(data) {
        return this.all(data).then(function(response) {
          return angular.isArray(response) ? response[0] : response;
        }, function() {
          return null;
        });
      },
      create: function(data) {
        return this.instance(deepExtend(copy(this.$config().defaults), data || {}));
      },

      // @todo Get methods for related objects
      $related: function(object) {
        if (!object.$links) return [];

        forEach(object.$links, function(url, name) {
          object.prototype[name] = function() {

          }
        });
      }
    },

    // Methods available on model instances
    $instance: {
      $save: function(data) {
        var method = this.$exists() ? 'PATCH' : 'POST';
        return $request(this, this.$model(), method, deepExtend(this, data ? copy(data) : {}));
      },
      $delete: function() {
        return $request(this, this.$model(), 'DELETE');
      },
      $reload: function() {
        return $request(this, this.$model(), 'GET');
      },
      $revert: function() {
        // @todo Reset to previously loaded state
      },
      $exists: function() {
        return !!expr(this, this.$model().$config().identity).get();
      }
    },

    // Methods available on model collections
    $collection: {
      add: function(object, data) {
        return object.$save(data || {});
      },
      remove: function(index) {
        index = (typeof index !== 'number') ? index = this.indexOf(index) : index;
        var self = this, result = self[index].$delete();
        result.then(function() { self.splice(index, 1); });
        return result;
      }
    }
  };

  function $request(object, model, method, data, headers) {
    var writeMethods = ['POST', 'PUT', 'PATCH'];
    var defaultHeaders = { 'Content-Type': 'application/json;charset=UTF-8' };
    var isWrite = writeMethods.indexOf(method) > -1;
    headers = extend({}, isWrite ? defaultHeaders : {}, headers);

    var deferred = q.defer(), params = {
      method:  method,
      url:     global.url(object || model),
      data:    data,
      headers: headers
    };

    if (!isWrite && isObject(data) && !isEmpty(data)) {
      params.url += (params.url.indexOf('?') > -1 ? '&' : '?') + serialize(data);
    }

    return extend(deferred.promise, {
      $response: null,
      $request: http(params).then(function(response) {
        deferred.promise.$response = response;
        deferred.resolve(autoBox(object, model, response.data));
      }, function(response) {
        if (model.$config().errors && response.data) {
          expr(object, model.$config().errors).set(response.data);
        }
        deferred.promise.$response = response;
        deferred.reject(response);
      })
    });
  }

  // Configures a new model, updates an existing model's settings, or updates global settings
  function config(name, options) {
    if (isObject(name)) {
      extend(global, name);
      return;
    }
    var previous = (registry[name] && registry[name].$config) ? registry[name].$config() : null,
        base = extend({}, previous ? extend({}, previous) : extend({}, DEFAULTS, DEFAULT_METHODS));

    options = deepExtend(copy(base), options);

    if (!options.url) {
      options.url = global.base.replace(/\/$/, '') + '/' + hyphenate(name);
    }
    registry[name] = new ModelClass(options);
  }

  extend(this, {

    model: function(name, options) {
      config(name, options);
      return this;
    },

    // Returns the model service
    $get: ['$http', '$parse', '$q', function($http, $parse, $q) {
      q = $q;
      http = $http;

      // Extracts a value from an object based on a string expression
      expr = function(obj, path) {
        var parsed = $parse(path);

        return {
          get: function() { return parsed(obj) },
          set: function(value) { return parsed.assign(obj, value); }
        };
      };

      // Adds, gets, or updates a named model configuration
      function ModelClassFactory(name, options) {
        if (!isUndef(options)) {
          return config(name, options);
        }
        return registry[name] || undefined;
      }

      ModelClassFactory.load = function(dst, promises) {
        forEach(promises, function(promise, name) {
          promise.then(function(value) {
            dst[name] = value;
          });
        });
        return q.all(promises);
      };

      return ModelClassFactory;
    }]
  });

  function ModelClass(options) {

    var scopedClassMethods = {
      $config: function() {
        return extend({}, options);
      },
      url: function() {
        return options.url;
      },
      instance: function(data) {
        options.$instance = inherit(new ModelInstance(this), options.$instance);
        return inherit(options.$instance, data || {});
      },
      collection: function(data, boxElements) {
        var owner = this, collection = extend([], extend({
          $model: function() { return owner; }
        }, options.$collection));

        if (data && data.length) {
          for (var i = data.length - 1; i >= 0; i--) {
            collection.unshift(boxElements ? this.instance(data[i]) : data[i]);
          }
        }
        return collection;
      }
    };

    extend(this, scopedClassMethods, options.$class);
  }

  function ModelInstance(owner) {
    this.$model = function() { return owner; };
  }

}).directive('link', ['model', function(model) {

  return {
    restrict: 'E',
    link: function(scope, element, attrs) {
      if (attrs.rel !== "resource" || !attrs.href || !attrs.name) {
        return;
      }
      model(attrs.name, { url: attrs.href, singleton: attrs.singleton ? true : false });
    }
  };

}]);

})(window, window.angular);<commit_msg>Add missing semicolons<commit_after>(function(window, angular, undefined) {
'use strict';

var noop   = angular.noop,
  forEach  = angular.forEach,
  extend   = angular.extend,
  copy     = angular.copy,
  isFunc   = angular.isFunction,
  isObject = angular.isObject,
  isArray  = angular.isArray,
  isUndef  = angular.isUndefined;

function deepExtend(dst, source) {
  for (var prop in source) {
    if (source[prop] && source[prop].constructor && source[prop].constructor === Object) {
      dst[prop] = dst[prop] || {};
      deepExtend(dst[prop], source[prop]);
      continue;
    }
    dst[prop] = source[prop];
  }
  return dst;
}

function isEmpty(obj) {
  var name;
  for (name in obj) return false;
  return true;
}

function inherit(parent, extra) {
  return extend(new (extend(function() {}, {prototype: parent}))(), extra);
}

function hyphenate(str) {
  var toLower = function($1) { return "-" + $1.toLowerCase(); };
  return str.replace(/([A-Z])/g, toLower).replace(/^-+/, '');
}

function serialize(obj, prefix) {
  var str = [];
  var enc = encodeURIComponent;

  for (var p in obj) {
    var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
    str.push(isObject(v) ? serialize(v, k) : enc(k) + "=" + enc(v));
  }
  return str.join("&");
}

angular.module('ur.model', []).provider('model', function() {

  var expr, // Used to evaluate expressions; initialized by the service invokation
      http, // Provider-level copy of $http service
      q;    // Provider-level copy of $q service

  // "Box" an object value in a model instance if it is present.
  // Otherwise, return an empty model object.
  function autoBox(object, model, data) {
    if (!object) {
      return isArray(data) ? model.collection(data, true) : model.instance(data);
    }
    if (isArray(data) && isArray(object)) {
      return model.collection(data, true);
    }
    if (data && JSON.stringify(data).length > 3) {
      return extend(object, data);
    }
    return object;
  }

  // Global registry of settings
  var global = {

    // Base URL prepended to generated URLs
    base: "",

    // Extract URL from object, or use default URL
    url: function(object) {
      if (object instanceof ModelClass) {
        return object.url();
      }
      if (object instanceof ModelInstance || isFunc(object.$model)) {
        var model = object.$model();
        return expr(object, model.$config().identity).get() || model.url();
      }
      throw new Error("Could not get URL for " + typeof object);
    }
  };

  // Master registry of application model configurations
  var registry = {};

  // Default configuration for new model classes
  var DEFAULTS = {

    // Default values which should be applied when creating a new model instance
    defaults: {},

    // The name of the key to the URL that identifies the object
    identity: "$links.self",

    // The name of the key to assign an object map of errors
    errors: "$errors"
  };

  var DEFAULT_METHODS = {

    // Methods available on the model class
    $class: {
      all: function(data, headers) {
        return $request(null, this, 'GET', data, headers);
      },
      first: function(data) {
        return this.all(data).then(function(response) {
          return angular.isArray(response) ? response[0] : response;
        }, function() {
          return null;
        });
      },
      create: function(data) {
        return this.instance(deepExtend(copy(this.$config().defaults), data || {}));
      },

      // @todo Get methods for related objects
      $related: function(object) {
        if (!object.$links) return [];

        forEach(object.$links, function(url, name) {
          object.prototype[name] = function() {

          };
        });
      }
    },

    // Methods available on model instances
    $instance: {
      $save: function(data) {
        var method = this.$exists() ? 'PATCH' : 'POST';
        return $request(this, this.$model(), method, deepExtend(this, data ? copy(data) : {}));
      },
      $delete: function() {
        return $request(this, this.$model(), 'DELETE');
      },
      $reload: function() {
        return $request(this, this.$model(), 'GET');
      },
      $revert: function() {
        // @todo Reset to previously loaded state
      },
      $exists: function() {
        return !!expr(this, this.$model().$config().identity).get();
      }
    },

    // Methods available on model collections
    $collection: {
      add: function(object, data) {
        return object.$save(data || {});
      },
      remove: function(index) {
        index = (typeof index !== 'number') ? index = this.indexOf(index) : index;
        var self = this, result = self[index].$delete();
        result.then(function() { self.splice(index, 1); });
        return result;
      }
    }
  };

  function $request(object, model, method, data, headers) {
    var writeMethods = ['POST', 'PUT', 'PATCH'];
    var defaultHeaders = { 'Content-Type': 'application/json;charset=UTF-8' };
    var isWrite = writeMethods.indexOf(method) > -1;
    headers = extend({}, isWrite ? defaultHeaders : {}, headers);

    var deferred = q.defer(), params = {
      method:  method,
      url:     global.url(object || model),
      data:    data,
      headers: headers
    };

    if (!isWrite && isObject(data) && !isEmpty(data)) {
      params.url += (params.url.indexOf('?') > -1 ? '&' : '?') + serialize(data);
    }

    return extend(deferred.promise, {
      $response: null,
      $request: http(params).then(function(response) {
        deferred.promise.$response = response;
        deferred.resolve(autoBox(object, model, response.data));
      }, function(response) {
        if (model.$config().errors && response.data) {
          expr(object, model.$config().errors).set(response.data);
        }
        deferred.promise.$response = response;
        deferred.reject(response);
      })
    });
  }

  // Configures a new model, updates an existing model's settings, or updates global settings
  function config(name, options) {
    if (isObject(name)) {
      extend(global, name);
      return;
    }
    var previous = (registry[name] && registry[name].$config) ? registry[name].$config() : null,
        base = extend({}, previous ? extend({}, previous) : extend({}, DEFAULTS, DEFAULT_METHODS));

    options = deepExtend(copy(base), options);

    if (!options.url) {
      options.url = global.base.replace(/\/$/, '') + '/' + hyphenate(name);
    }
    registry[name] = new ModelClass(options);
  }

  extend(this, {

    model: function(name, options) {
      config(name, options);
      return this;
    },

    // Returns the model service
    $get: ['$http', '$parse', '$q', function($http, $parse, $q) {
      q = $q;
      http = $http;

      // Extracts a value from an object based on a string expression
      expr = function(obj, path) {
        var parsed = $parse(path);

        return {
          get: function() { return parsed(obj); },
          set: function(value) { return parsed.assign(obj, value); }
        };
      };

      // Adds, gets, or updates a named model configuration
      function ModelClassFactory(name, options) {
        if (!isUndef(options)) {
          return config(name, options);
        }
        return registry[name] || undefined;
      }

      ModelClassFactory.load = function(dst, promises) {
        forEach(promises, function(promise, name) {
          promise.then(function(value) {
            dst[name] = value;
          });
        });
        return q.all(promises);
      };

      return ModelClassFactory;
    }]
  });

  function ModelClass(options) {

    var scopedClassMethods = {
      $config: function() {
        return extend({}, options);
      },
      url: function() {
        return options.url;
      },
      instance: function(data) {
        options.$instance = inherit(new ModelInstance(this), options.$instance);
        return inherit(options.$instance, data || {});
      },
      collection: function(data, boxElements) {
        var owner = this, collection = extend([], extend({
          $model: function() { return owner; }
        }, options.$collection));

        if (data && data.length) {
          for (var i = data.length - 1; i >= 0; i--) {
            collection.unshift(boxElements ? this.instance(data[i]) : data[i]);
          }
        }
        return collection;
      }
    };

    extend(this, scopedClassMethods, options.$class);
  }

  function ModelInstance(owner) {
    this.$model = function() { return owner; };
  }

}).directive('link', ['model', function(model) {

  return {
    restrict: 'E',
    link: function(scope, element, attrs) {
      if (attrs.rel !== "resource" || !attrs.href || !attrs.name) {
        return;
      }
      model(attrs.name, { url: attrs.href, singleton: attrs.singleton ? true : false });
    }
  };

}]);

})(window, window.angular);<|endoftext|>


<commit_before>var engine = require('engine'),
    utils = require('./utils.js');

var Analytics = function () {
    var self = this,
        supportSendBeacon = !!navigator.sendBeacon,
        timeout = 1000,
        beacon = null,
        animatronUrl = utils.makeApiUrl('/analytics/player');

    self.enabled = animatronUrl != null && animatronUrl.indexOf('animatron-test') >= 0;
    self.queue = [];

    var event = function () {
        if (self.queue.length > 0) {
            var data = JSON.stringify(self.queue);
            self.queue = [];

            if (supportSendBeacon) {
                navigator.sendBeacon(animatronUrl, data);
            } else {
                sendViaGif(data);
            }
        }
        if (!supportSendBeacon) {
            setTimeout(event, timeout);
        }
    };

    var sendViaGif = function (data) {
        if (!beacon) {
            beacon = engine.createStatImg();
        }
        beacon.src = animatronUrl + '?data=' + encodeURIComponent(data);
    };
    if (self.enabled) {
        if (!supportSendBeacon) {
            engine.onDocReady(event);
        }
        window.addEventListener('unload', event, false);
    }

    this.trackPlayingStart = this.trackPlayer('playing_start');
    this.trackPlayingPause = this.trackPlayer('playing_pause');
    this.trackPlayingComplete = this.trackPlayer('playing_complete');
};

Analytics.prototype.track = function track(name, opts) {
    opts = opts || {};
    opts.name = name;
    opts.referer = document.referrer;
    opts.lang = navigator.language || navigator.userLanguage;
    opts.url = location.href;
    opts.screenHeight = screen.height;
    opts.screenWidth = screen.width;
    opts.windowHeight = window.innerHeight;
    opts.windowWidth = window.innerWidth;
    opts.timestamp = new Date().getTime();
    if (this.enabled) {
        this.queue.push(opts);
    }
};

Analytics.prototype.trackPlayer = function trackPlayer(name) {
    return function (player) {
        var opts = {viewId: player.viewId, projectId: player.anim.meta._anm_id, time: player.state.time};
        this.track(name, opts);
    }.bind(this);
};

Analytics.prototype.getObjectId = function () {
    var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
    return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () {
            return (Math.random() * 16 | 0).toString(16);
        }).toLowerCase();
};

module.exports = new Analytics();
<commit_msg>use interval instead of timout<commit_after>var engine = require('engine'),
    utils = require('./utils.js');

var Analytics = function () {
    var self = this,
        supportSendBeacon = !!navigator.sendBeacon,
        interval = 1000,
        beacon = null,
        animatronUrl = utils.makeApiUrl('/analytics/player');

    self.enabled = animatronUrl != null && animatronUrl.indexOf('animatron-test') >= 0;
    self.queue = [];

    var event = function () {
        if (self.queue.length > 0) {
            var data = JSON.stringify(self.queue);
            self.queue = [];

            if (supportSendBeacon) {
                navigator.sendBeacon(animatronUrl, data);
            } else {
                sendViaGif(data);
            }
        }
    };

    var sendViaGif = function (data) {
        if (!beacon) {
            beacon = engine.createStatImg();
        }
        beacon.src = animatronUrl + '?data=' + encodeURIComponent(data);
    };
    if (self.enabled) {
        if (!supportSendBeacon) {
            setInterval(event, interval);
        }
        window.addEventListener('unload', event, false);
    }

    this.trackPlayingStart = this.trackPlayer('playing_start');
    this.trackPlayingPause = this.trackPlayer('playing_pause');
    this.trackPlayingComplete = this.trackPlayer('playing_complete');
};

Analytics.prototype.track = function track(name, opts) {
    opts = opts || {};
    opts.name = name;
    opts.referer = document.referrer;
    opts.lang = navigator.language || navigator.userLanguage;
    opts.url = location.href;
    opts.screenHeight = screen.height;
    opts.screenWidth = screen.width;
    opts.windowHeight = window.innerHeight;
    opts.windowWidth = window.innerWidth;
    opts.timestamp = new Date().getTime();
    if (this.enabled) {
        this.queue.push(opts);
    }
};

Analytics.prototype.trackPlayer = function trackPlayer(name) {
    return function (player) {
        var opts = {viewId: player.viewId, projectId: player.anim.meta._anm_id, time: player.state.time};
        this.track(name, opts);
    }.bind(this);
};

Analytics.prototype.getObjectId = function () {
    var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
    return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () {
            return (Math.random() * 16 | 0).toString(16);
        }).toLowerCase();
};

module.exports = new Analytics();
<|endoftext|>


<commit_before>import React from 'react';

const Status = ({url, title, validUrl, working, status, progressBar}) => (

     { pdfFunctions.openURL(url); }}>
      {title} -
      {validUrl?
         :

      }

    {validUrl?working:''}

);

Status.propTypes = {
  title: React.PropTypes.string,
  status: React.PropTypes.number,
  working: React.PropTypes.string,
  url: React.PropTypes.string,
  validUrl: React.PropTypes.bool,
  progressBar: React.PropTypes.number,
};
export default Status;
<commit_msg>bugfix_loading_message_solved<commit_after>import React from 'react';

const Status = ({url, title, validUrl, working, status, progressBar}) => (

     { pdfFunctions.openURL(url); }}>
      {title} -
      {validUrl?
         :

      }

    {validUrl && progressBar!==0 && status===0? 'Loading Page':''}

);

Status.propTypes = {
  title: React.PropTypes.string,
  status: React.PropTypes.number,
  working: React.PropTypes.string,
  url: React.PropTypes.string,
  validUrl: React.PropTypes.bool,
  progressBar: React.PropTypes.number,
};
export default Status;
<|endoftext|>


<commit_before>import React, {PureComponent} from 'react';
import {Avatar, AppBar, Drawer, FloatingActionButton, FontIcon, IconButton, MenuItem, FlatButton, Subheader, TextField} from 'material-ui';
import {ContentAdd} from 'material-ui/svg-icons';
import {capitalize} from 'lodash';
import 'material-design-lite/material';
import i18n from 'i18next';

export class Layout extends PureComponent {

    constructor(props) {
        super(props);
        this.state = {
            isShown: false,
            buttonClassName: 'add-button',
            drawerIsOpen: false
        };
    }

    componentDidMount() {
        const loggedUser = JSON.parse(localStorage.getItem('user'));
        if(loggedUser.role === 'admin') {
            this.showFAB();
        }
    }

    // This will give or remove animation class on the add button
    showFAB = () => {
        const {isShown, buttonClassName} = this.state;
        if (this.refs.createFAB.props.className === 'add-button') {
            this.setState({buttonClassName: 'add-button show', isShown: !isShown})
        } else {
            this.setState({buttonClassName: 'add-button', isShown: !isShown})
        }
    }

    // This will return the correct title
    getTitle(pathname) {
        const pathArray = pathname.split("/");
        return capitalize(i18n.t(pathArray[1]));
    }

    setMenuItemStyle(name) {
        return this.checkMenuItem(name) ? {
            backgroundColor: 'rgba(0,0,0,0.0980392)'
        } : null;
    }

    handleItemClick(path, ref) {
        this.toggleDrawer();
        if(this.refs[ref].props.focusState !== 'focused') {
            switch (path) {
                case '/':
                this.props.router.push(path);
                break;
                case '/tutorial':
                this.props.router.push(`${path}/1`);
                break;
                default:
                return null;
            }
        }
    };

    checkMenuItem(name) {
        return name === this.getTitle(this.props.location.pathname);
    }

    toggleDrawer = () => this.setState({drawerIsOpen: !this.state.drawerIsOpen});

    handleEditButtonClick = () => {
        // Here we save an article, we get its id then we got on the edit page
        // gotta call a service here
        let id;
        // id = this.props.saveArticle();
        this.props.router.push(!id ? `edit-article/1` : `edit-article/${id}`);
    }

    onButtonLogOutClick = () => {
        console.log('Déconnexion');
        localStorage.removeItem('user');
        this.props.router.push('/');
    }

    render() {
        const {isShown, buttonClassName, drawerIsOpen} = this.state;
        return (

                <AppBar
                    onLeftIconButtonTouchTap={this.toggleDrawer}
                    style={{position: 'fixed', paddingTop: '6px'}}
                    className='global-appbar'
                    title={

                            {this.getTitle(this.props.location.pathname)}

                                    search
                                    <TextField
                                        id='search-bar'
                                        inputStyle={{color: 'white'}}
                                        underlineShow={false}
                                        hintText={`${i18n.t('search.placeholder')}`}
                                        hintStyle={{color: 'rgba(255,255,255,0.6)'}}
                                        style= {{marginLeft: 2, width: '100%'}}
                                        onFocus={e => {
                                            e.target.parentNode.parentNode.style.backgroundColor = 'transparent';
                                            e.target.parentNode.parentNode.style.boxShadow = '0px 1px 3px rgba(0,0,0,0.3)';
                                        }}

                                        onBlur={e => {
                                            e.target.parentNode.parentNode.style.backgroundColor = 'rgba(0,0,0,0.18)';
                                            e.target.parentNode.parentNode.style.boxShadow = undefined;
                                        }}
                                        />

                    }
                    iconElementRight={<FlatButton label='Déconnexion' onClick={this.onButtonLogOutClick} style={{marginTop: '3px', verticalAlign: 'middle'}}primary={true} />}
                    iconStyleRight={{alignSelf: 'center', marginTop: '0px', marginRight: '10px'}}
                    />
                <Drawer docked={false} width={300} open={drawerIsOpen} onRequestChange={(drawerIsOpen) => this.setState({drawerIsOpen})}>
                    <AppBar
                        style={{position: 'fixed', paddingTop: '6px'}}
                        iconElementLeft={<IconButton iconClassName='material-icons'>arrow_back</IconButton>}
                        onTitleTouchTap={this.toggleDrawer}
                        onLeftIconButtonTouchTap={this.toggleDrawer}
                        className='menu-appbar'
                        title='Menu'
                        />

                    <MenuItem style={this.setMenuItemStyle('Accueil')} focusState={this.checkMenuItem('Accueil') ? 'focused' : 'none'} ref='menuItemHome'onTouchTap={() => this.handleItemClick('/', 'menuItemHome')} primaryText='Accueil' leftIcon={<FontIcon className='material-icons'>home</FontIcon>}/>
                    <MenuItem style={this.setMenuItemStyle('Tutoriel')} focusState={this.checkMenuItem('Tutoriel') ? 'focused' : 'none'} ref='menuItemTuto' onTouchTap={() => this.handleItemClick('/tutorial', 'menuItemTuto')} primaryText='Tutoriels' leftIcon={<FontIcon className='material-icons'>class</FontIcon>}/>
                </Drawer>
                {this.props.children}
                <FloatingActionButton onClick={this.handleEditButtonClick} ref='createFAB' className={buttonClassName} secondary={true}><ContentAdd /></FloatingActionButton>

        )
    }
}
<commit_msg>Minor update<commit_after>import React, {PureComponent} from 'react';
import {Avatar, AppBar, Drawer, FloatingActionButton, FontIcon, IconButton, MenuItem, FlatButton, Subheader, TextField} from 'material-ui';
import {ContentAdd} from 'material-ui/svg-icons';
import {capitalize} from 'lodash';
import 'material-design-lite/material';
import i18next from 'i18next';

export class Layout extends PureComponent {

    constructor(props) {
        super(props);
        this.state = {
            isShown: false,
            buttonClassName: 'add-button',
            drawerIsOpen: false
        };
    }

    componentDidMount() {
        const loggedUser = JSON.parse(localStorage.getItem('user'));
        if(loggedUser.role === 'admin') {
            this.showFAB();
        }
    }

    // This will give or remove animation class on the add button
    showFAB = () => {
        const {isShown, buttonClassName} = this.state;
        if (this.refs.createFAB.props.className === 'add-button') {
            this.setState({buttonClassName: 'add-button show', isShown: !isShown})
        } else {
            this.setState({buttonClassName: 'add-button', isShown: !isShown})
        }
    }

    // This will return the correct title
    getTitle(pathname) {
        const pathArray = pathname.split("/");
        return capitalize(i18next.t(pathArray[1]));
    }

    setMenuItemStyle(name) {
        return this.checkMenuItem(name) ? {
            backgroundColor: 'rgba(0,0,0,0.0980392)'
        } : null;
    }

    handleItemClick(path, ref) {
        this.toggleDrawer();
        if(this.refs[ref].props.focusState !== 'focused') {
            switch (path) {
                case '/':
                this.props.router.push(path);
                break;
                case '/tutorial':
                this.props.router.push(`${path}/1`);
                break;
                default:
                return null;
            }
        }
    };

    checkMenuItem(name) {
        return name === this.getTitle(this.props.location.pathname);
    }

    toggleDrawer = () => this.setState({drawerIsOpen: !this.state.drawerIsOpen});

    handleEditButtonClick = () => {
        // Here we save an article, we get its id then we got on the edit page
        // gotta call a service here
        let id;
        // id = this.props.saveArticle();
        this.props.router.push(!id ? `edit-article/1` : `edit-article/${id}`);
    }

    onButtonLogOutClick = () => {
        console.log('Déconnexion');
        localStorage.removeItem('user');
        this.props.router.push('/');
    }

    render() {
        const {isShown, buttonClassName, drawerIsOpen} = this.state;
        console.log(window.innerWidth);
        return (

                <AppBar
                    onLeftIconButtonTouchTap={this.toggleDrawer}
                    style={{position: 'fixed', paddingTop: '6px'}}
                    className='global-appbar'
                    title={

                            {this.getTitle(this.props.location.pathname)}

                                    search
                                    <TextField
                                        id='search-bar'
                                        inputStyle={{color: 'white'}}
                                        underlineShow={false}
                                        hintText={`${i18next.t('search.placeholder')}`}
                                        hintStyle={{color: 'rgba(255,255,255,0.6)'}}
                                        style= {{marginLeft: 2, width: '100%'}}
                                        onFocus={e => {
                                            e.target.parentNode.parentNode.style.backgroundColor = 'transparent';
                                            e.target.parentNode.parentNode.style.boxShadow = '0px 1px 3px rgba(0,0,0,0.3)';
                                        }}

                                        onBlur={e => {
                                            e.target.parentNode.parentNode.style.backgroundColor = 'rgba(0,0,0,0.18)';
                                            e.target.parentNode.parentNode.style.boxShadow = undefined;
                                        }}
                                        />

                    }
                    iconElementRight={<FlatButton label='Déconnexion' onClick={this.onButtonLogOutClick} style={{marginTop: '3px', verticalAlign: 'middle'}}primary={true} />}
                    iconStyleRight={{alignSelf: 'center', marginTop: '0px', marginRight: '10px'}}
                    />
                <Drawer docked={false} width={300} open={drawerIsOpen} onRequestChange={(drawerIsOpen) => this.setState({drawerIsOpen})}>
                    <AppBar
                        style={{position: 'fixed', paddingTop: '6px'}}
                        iconElementLeft={<IconButton iconClassName='material-icons'>arrow_back</IconButton>}
                        onTitleTouchTap={this.toggleDrawer}
                        onLeftIconButtonTouchTap={this.toggleDrawer}
                        className='menu-appbar'
                        title='Menu'
                        />

                    <MenuItem style={this.setMenuItemStyle('Accueil')} focusState={this.checkMenuItem('Accueil') ? 'focused' : 'none'} ref='menuItemHome'onTouchTap={() => this.handleItemClick('/', 'menuItemHome')} primaryText='Accueil' leftIcon={<FontIcon className='material-icons'>home</FontIcon>}/>
                    <MenuItem style={this.setMenuItemStyle('Tutoriel')} focusState={this.checkMenuItem('Tutoriel') ? 'focused' : 'none'} ref='menuItemTuto' onTouchTap={() => this.handleItemClick('/tutorial', 'menuItemTuto')} primaryText='Tutoriels' leftIcon={<FontIcon className='material-icons'>class</FontIcon>}/>
                </Drawer>
                {this.props.children}
                <FloatingActionButton onClick={this.handleEditButtonClick} ref='createFAB' className={buttonClassName} secondary={true}><ContentAdd /></FloatingActionButton>

        )
    }
}
<|endoftext|>