<commit_before>/* eslint-disable no-shadow */
import React, { Component } from 'react';
import { graphql, commitMutation } from 'react-relay/compat';
import { Store } from 'react-relay/classic';
import { Timeline } from '@meedan/check-ui';
import qs from 'qs';

const environment = Store;

const updateComment = (id, text, callback) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineUpdateCommentMutation($input: UpdateCommentInput!) {
      updateComment(input: $input) {
        comment {
          id
          dbid
          text
        }
      }
    }
  `,
  variables: {
    input: { id, text, clientMutationId: `m${Date.now()}` },
  },
  optimisticResponse: {
    updateComment: {
      comment: {
        id,
        text,
      },
    },
  },
  onCompleted: (data, errors) => callback && callback(data, errors),
});

const destroyComment = (id, annotated_id, callback) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineDestroyCommentMutation($input: DestroyCommentInput!) {
      destroyComment(input: $input) {
        deletedId
      }
    }
  `,
  variables: {
    input: { id, clientMutationId: `m${Date.now()}` },
  },
  configs: [
    {
      type: 'NODE_DELETE',
      parentName: 'project_media',
      parentID: annotated_id,
      connectionName: 'comments',
      deletedIDFieldName: 'deletedId',
    },
  ],
  onCompleted: (data, errors) => callback && callback(data, errors),
});

const createComment =
  (text, fragment, annotated_id, parentID, callback) => {
    console.log({
      text, fragment, annotated_id, parentID, callback,
    });
    return commitMutation(environment, {
      mutation: graphql`
        mutation MediaTimelineCreateCommentMutation($input: CreateCommentInput!) {
          createComment(input: $input) {
            commentEdge {
              cursor
              __typename
              node {
                id
                text
                dbid
                annotator {
                  id
                  name
                  profile_image
                }
                text
                parsed_fragment
              }
            }
          }
        }
      `,
      variables: {
        input: {
          annotated_id, text, fragment, clientMutationId: `m${Date.now()}`, annotated_type: 'Comment',
        },
      },
      configs: [
        {
          type: 'RANGE_ADD',
          parentName: 'Comment',
          parentID,
          edgeName: 'commentEdge',
          connectionName: 'annotations',
          rangeBehaviors: () => ('append'),
          connectionInfo: [{
            key: 'Comment_comments',
            rangeBehavior: 'append',
          }],
        },
      ],
      onCompleted: (data, errors) => callback && callback(data, errors),
    });
  };

const createCommentThread =
  (text, fragment, annotated_id, parentID, callback) => {
    console.log({
      text, fragment, annotated_id, parentID, callback,
    });
    return commitMutation(environment, {
      mutation: graphql`
        mutation MediaTimelineCreateCommentThreadMutation($input: CreateCommentInput!) {
          createComment(input: $input) {
            commentEdge {
              cursor
              __typename
              node {
                id
                text
                dbid
                annotator {
                  id
                  name
                  profile_image
                }
                text
                parsed_fragment
                comments: annotations(first: 10000, annotation_type: "comment") {
                  edges {
                    node {
                      ... on Comment {
                        id
                        created_at
                        text
                        annotator {
                          id
                          name
                          profile_image
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      `,
      variables: {
        input: {
          annotated_id, text, fragment, clientMutationId: `m${Date.now()}`, annotated_type: 'ProjectMedia',
        },
      },
      configs: [
        {
          type: 'RANGE_ADD',
          parentName: 'project_media',
          parentID,
          edgeName: 'commentEdge',
          connectionName: 'annotations',
          rangeBehaviors: () => ('append'),
          connectionInfo: [{
            key: 'ProjectMedia_comments',
            rangeBehavior: 'append',
          }],
        },
      ],
      onCompleted: (data, errors) => callback && callback(data, errors),
    });
  };

const createTag = (tag, fragment, annotated_id, parentID, callback) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineCreateInstanceMutation($input: CreateTagInput!) {
      createTag(input: $input) {
        project_media {
          id
        }
        tagEdge {
          node {
            id
            fragment
            tag_text_object {
              id
              text
            }
          }
        }
      }
    }
  `,
  variables: {
    input: {
      tag, fragment, annotated_id, clientMutationId: `m${Date.now()}`, annotated_type: 'ProjectMedia',
    },
  },
  configs: [
    {
      type: 'RANGE_ADD',
      parentName: 'project_media',
      parentID,
      edgeName: 'tagEdge',
      connectionName: 'tags',
      rangeBehaviors: () => ('prepend'),
      connectionInfo: [{
        key: 'ProjectMedia_tags',
        rangeBehavior: 'prepend',
      }],
    },
  ],
  onCompleted: (data, errors) => callback && callback(data, errors),
});

const retimeTag = (id, fragment) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineFragmentMutation($input: UpdateTagInput!) {
      updateTag(input: $input) {
        tag {
          id
          fragment
        }
      }
    }
  `,
  variables: {
    input: { id, fragment, clientMutationId: `m${Date.now()}` },
  },
  optimisticResponse: {
    updateTag: {
      tag: {
        id,
        fragment,
      },
    },
  },
});

const renameTag = (id, text, callback) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineTextMutation($input: UpdateTagTextInput!) {
      updateTagText(input: $input) {
        tag_text {
          id
          text
        }
      }
    }
  `,
  variables: {
    input: { id, text, clientMutationId: `m${Date.now()}` },
  },
  onCompleted: (data, errors) => callback && callback(data, errors),
  optimisticResponse: {
    updateTagText: {
      tag_text: {
        id,
        text,
      },
    },
  },
});

const destroyTag = (id, annotated_id) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineDestroyMutation($input: DestroyTagTextInput!) {
      destroyTagText(input: $input) {
        deletedId
      }
    }
  `,
  variables: {
    input: { id, clientMutationId: `m${Date.now()}` },
  },
  configs: [
    {
      type: 'NODE_DELETE',
      parentName: 'project_media',
      parentID: annotated_id,
      connectionName: 'tags',
      deletedIDFieldName: 'deletedId',
    },
  ],
});

const entityCreate = (type, payload, mediaId, parentId, callback) => {
  switch (type) {
  case 'tag':
    createTag(payload[`project_${type}`].name, payload.fragment, `${mediaId}`, parentId, callback);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const entityUpdate = (type, entityId, payload, callback) => {
  switch (type) {
  case 'tag':
    renameTag(entityId, payload.project_tag.name, callback);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const entityDelete = (type, entityId, tags, mediaId, callback) => {
  switch (type) {
  case 'tag':
    tags.forEach(({ id }) => destroyTag(id, mediaId));
    if (callback) callback();
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const instanceCreate = (type, id, name, payload, mediaId, parentId, callback) => {
  switch (type) {
  case 'tag':
    createTag(name, payload.fragment, mediaId, parentId, callback);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const instanceUpdate = (type, entityId, instanceId, fragment) => {
  switch (type) {
  case 'tag':
    retimeTag(instanceId, fragment);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const instanceDelete = (type, instanceId, mediaId) => {
  switch (type) {
  case 'tag':
    destroyTag(instanceId, mediaId);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const instanceClip = (type, entityId, instanceId) => {
  switch (type) {
  case 'tag':
    console.warn(`TODO instance ${instanceId} -> clip`);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const playlistLaunch = (type, data) => {
  switch (type) {
  case 'tags':
    console.warn('TODO playlist tags', data);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const commentThreadCreate = (time, text, mediaId, parentID, callback) => {
  createCommentThread(text, `t=${time}`, `${mediaId}`, parentID, callback);
};

const commentThreadDelete = (id, annotated_id, callback) => {
  destroyComment(id, annotated_id, callback);
};

const commentCreate = (id, text, parentID, callback) => {
  console.log({
    id, text, parentID, callback,
  });
  createComment(text, null, `${id}`, parentID, callback);
};

const commentEdit = (threadId, commentId, text, callback) => {
  console.log({
    threadId, commentId, text, callback,
  });
  updateComment(commentId, text, callback);
};

const commentDelete = (threadId, commentId) => {
  destroyComment(commentId, threadId);
};

class MediaTimeline extends Component {
  render() {
    const {
      media: {
        id: mediaId, dbid,
        tags: { edges = [] },
        comments,
      },
      duration, time, setPlayerState,
      currentUser: { name: first_name, id: userId, profile_image: profile_img_url },
    } = this.props;

    const projecttags = [];
    const entities = {};

    edges
      .filter(({ node }) => !!node)
      .forEach(({ node: { id: instance, fragment, tag_text_object } }) => {
        if (!tag_text_object) return;

        const { id, text: name } = tag_text_object;
        if (!fragment) {
          projecttags.push({ id, name });
          return;
        }

        if (!entities[id]) {
          entities[id] = {
            id, name, project_tag: { id, name }, instances: [],
          };
        }

        const { t = '0,0', type = 'tag' } = qs.parse(fragment);
        const [start_seconds, end_seconds] = t.split(',').map(n => parseFloat(n));

        entities[id].type = type;
        entities[id].instances.push({ start_seconds, end_seconds, id: instance });
      });

    const commentThreads = comments.edges.map(({
      node: {
        id: thread_id, dbid, text, parsed_fragment,
        annotator: {
          id: annoId, name, profile_image,
        },
        comments,
      },
    }) => ({
      id: thread_id,
      dbid,
      text,
      start_seconds: parsed_fragment.t[0],
      user: {
        id: annoId,
        first_name: name,
        last_name: '',
        profile_img_url: profile_image,
      },
      replies: comments && comments.edges ? comments.edges.map(({
        node: {
          id, created_at, text,
          annotator: {
            id: annoId, name, profile_image,
          },
        },
      }) => ({
        id,
        thread_id,
        created_at,
        text,
        start_seconds: 0,
        user: {
          id: annoId,
          first_name: name,
          last_name: '',
          profile_img_url: profile_image,
        },
      })).sort((a, b) => a.created_at - b.created_at) : [],
    }));

    const data = {
      commentThreads,
      project: {
        projectclips: [],
        projectplaces: [],
        projecttags,
      },
      videoClips: [],
      videoPlaces: [],
      videoTags: Object.values(entities).filter(({ type }) => type === 'tag' || type === 'tags'),
      user: {
        first_name,
        id: userId,
        last_name: '',
        profile_img_url,
      },
    };

    return (
      <Timeline
        currentTime={time}
        data={data}
        duration={duration}
        onBeforeCommentThreadCreate={(a, b, c, d, e) => console.log(a, b, c, d, e)}
        onCommentCreate={(threadId, text, callback) =>
          commentCreate(commentThreads.find(({ id }) =>
            id === threadId).dbid, text, threadId, callback)}
        onCommentDelete={(threadId, commentId) => commentDelete(threadId, commentId)}
        onCommentEdit={(threadId, commentId, text, callback) =>
          commentEdit(threadId, commentId, text, callback)}
        onCommentThreadCreate={
          (t, text, callback) => commentThreadCreate(t, text, dbid, mediaId, callback)
        }
        onCommentThreadDelete={(id, callback) => commentThreadDelete(id, mediaId, callback)}
        onEntityCreate={
          (type, payload, callback) => entityCreate(type, payload, dbid, mediaId, callback)
        }
        onEntityDelete={
          (type, entityId, callback) =>
            entityDelete(type, entityId, data.videoTags.find(({ id }) =>
              entityId === id).instances, dbid, callback)
        }
        onEntityUpdate={
          (type, entityId, payload, callback) => entityUpdate(type, entityId, payload, callback)
        }
        onInstanceClip={(type, entityId, instanceId) => instanceClip(type, entityId, instanceId)}
        onInstanceCreate={
          (type, id, payload, callback) =>
            instanceCreate(type, id, data.videoTags.find(({ id: _id }) =>
              _id === id).name, payload, dbid, mediaId, callback)
        }
        onInstanceDelete={(type, entityId, instanceId) => instanceDelete(type, instanceId, dbid)}
        onInstanceUpdate={(type, entityId, instanceId, { start_seconds, end_seconds }) => instanceUpdate(type, entityId, instanceId, `t=${start_seconds},${end_seconds}&type=${type}`)}
        onPlaylistLaunch={type => playlistLaunch(type, data)}
        onTimeChange={seekTo => setPlayerState({ seekTo })}
      />
    );
  }
}

export default MediaTimeline;
<commit_msg>add tag instance annotated_id string fix<commit_after>/* eslint-disable no-shadow */
import React, { Component } from 'react';
import { graphql, commitMutation } from 'react-relay/compat';
import { Store } from 'react-relay/classic';
import { Timeline } from '@meedan/check-ui';
import qs from 'qs';

const environment = Store;

const updateComment = (id, text, callback) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineUpdateCommentMutation($input: UpdateCommentInput!) {
      updateComment(input: $input) {
        comment {
          id
          dbid
          text
        }
      }
    }
  `,
  variables: {
    input: { id, text, clientMutationId: `m${Date.now()}` },
  },
  optimisticResponse: {
    updateComment: {
      comment: {
        id,
        text,
      },
    },
  },
  onCompleted: (data, errors) => callback && callback(data, errors),
});

const destroyComment = (id, annotated_id, callback) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineDestroyCommentMutation($input: DestroyCommentInput!) {
      destroyComment(input: $input) {
        deletedId
      }
    }
  `,
  variables: {
    input: { id, clientMutationId: `m${Date.now()}` },
  },
  configs: [
    {
      type: 'NODE_DELETE',
      parentName: 'project_media',
      parentID: annotated_id,
      connectionName: 'comments',
      deletedIDFieldName: 'deletedId',
    },
  ],
  onCompleted: (data, errors) => callback && callback(data, errors),
});

const createComment =
  (text, fragment, annotated_id, parentID, callback) => {
    console.log({
      text, fragment, annotated_id, parentID, callback,
    });
    return commitMutation(environment, {
      mutation: graphql`
        mutation MediaTimelineCreateCommentMutation($input: CreateCommentInput!) {
          createComment(input: $input) {
            commentEdge {
              cursor
              __typename
              node {
                id
                text
                dbid
                annotator {
                  id
                  name
                  profile_image
                }
                text
                parsed_fragment
              }
            }
          }
        }
      `,
      variables: {
        input: {
          annotated_id, text, fragment, clientMutationId: `m${Date.now()}`, annotated_type: 'Comment',
        },
      },
      configs: [
        {
          type: 'RANGE_ADD',
          parentName: 'Comment',
          parentID,
          edgeName: 'commentEdge',
          connectionName: 'annotations',
          rangeBehaviors: () => ('append'),
          connectionInfo: [{
            key: 'Comment_comments',
            rangeBehavior: 'append',
          }],
        },
      ],
      onCompleted: (data, errors) => callback && callback(data, errors),
    });
  };

const createCommentThread =
  (text, fragment, annotated_id, parentID, callback) => {
    console.log({
      text, fragment, annotated_id, parentID, callback,
    });
    return commitMutation(environment, {
      mutation: graphql`
        mutation MediaTimelineCreateCommentThreadMutation($input: CreateCommentInput!) {
          createComment(input: $input) {
            commentEdge {
              cursor
              __typename
              node {
                id
                text
                dbid
                annotator {
                  id
                  name
                  profile_image
                }
                text
                parsed_fragment
                comments: annotations(first: 10000, annotation_type: "comment") {
                  edges {
                    node {
                      ... on Comment {
                        id
                        created_at
                        text
                        annotator {
                          id
                          name
                          profile_image
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      `,
      variables: {
        input: {
          annotated_id, text, fragment, clientMutationId: `m${Date.now()}`, annotated_type: 'ProjectMedia',
        },
      },
      configs: [
        {
          type: 'RANGE_ADD',
          parentName: 'project_media',
          parentID,
          edgeName: 'commentEdge',
          connectionName: 'annotations',
          rangeBehaviors: () => ('append'),
          connectionInfo: [{
            key: 'ProjectMedia_comments',
            rangeBehavior: 'append',
          }],
        },
      ],
      onCompleted: (data, errors) => callback && callback(data, errors),
    });
  };

const createTag = (tag, fragment, annotated_id, parentID, callback) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineCreateInstanceMutation($input: CreateTagInput!) {
      createTag(input: $input) {
        project_media {
          id
        }
        tagEdge {
          node {
            id
            fragment
            tag_text_object {
              id
              text
            }
          }
        }
      }
    }
  `,
  variables: {
    input: {
      tag, fragment, annotated_id, clientMutationId: `m${Date.now()}`, annotated_type: 'ProjectMedia',
    },
  },
  configs: [
    {
      type: 'RANGE_ADD',
      parentName: 'project_media',
      parentID,
      edgeName: 'tagEdge',
      connectionName: 'tags',
      rangeBehaviors: () => ('prepend'),
      connectionInfo: [{
        key: 'ProjectMedia_tags',
        rangeBehavior: 'prepend',
      }],
    },
  ],
  onCompleted: (data, errors) => callback && callback(data, errors),
});

const retimeTag = (id, fragment) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineFragmentMutation($input: UpdateTagInput!) {
      updateTag(input: $input) {
        tag {
          id
          fragment
        }
      }
    }
  `,
  variables: {
    input: { id, fragment, clientMutationId: `m${Date.now()}` },
  },
  optimisticResponse: {
    updateTag: {
      tag: {
        id,
        fragment,
      },
    },
  },
});

const renameTag = (id, text, callback) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineTextMutation($input: UpdateTagTextInput!) {
      updateTagText(input: $input) {
        tag_text {
          id
          text
        }
      }
    }
  `,
  variables: {
    input: { id, text, clientMutationId: `m${Date.now()}` },
  },
  onCompleted: (data, errors) => callback && callback(data, errors),
  optimisticResponse: {
    updateTagText: {
      tag_text: {
        id,
        text,
      },
    },
  },
});

const destroyTag = (id, annotated_id) => commitMutation(environment, {
  mutation: graphql`
    mutation MediaTimelineDestroyMutation($input: DestroyTagTextInput!) {
      destroyTagText(input: $input) {
        deletedId
      }
    }
  `,
  variables: {
    input: { id, clientMutationId: `m${Date.now()}` },
  },
  configs: [
    {
      type: 'NODE_DELETE',
      parentName: 'project_media',
      parentID: annotated_id,
      connectionName: 'tags',
      deletedIDFieldName: 'deletedId',
    },
  ],
});

const entityCreate = (type, payload, mediaId, parentId, callback) => {
  switch (type) {
  case 'tag':
    createTag(payload[`project_${type}`].name, payload.fragment, `${mediaId}`, parentId, callback);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const entityUpdate = (type, entityId, payload, callback) => {
  switch (type) {
  case 'tag':
    renameTag(entityId, payload.project_tag.name, callback);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const entityDelete = (type, entityId, tags, mediaId, callback) => {
  switch (type) {
  case 'tag':
    tags.forEach(({ id }) => destroyTag(id, mediaId));
    if (callback) callback();
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const instanceCreate = (type, id, name, payload, mediaId, parentId, callback) => {
  switch (type) {
  case 'tag':
    createTag(name, payload.fragment, `${mediaId}`, parentId, callback);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const instanceUpdate = (type, entityId, instanceId, fragment) => {
  switch (type) {
  case 'tag':
    retimeTag(instanceId, fragment);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const instanceDelete = (type, instanceId, mediaId) => {
  switch (type) {
  case 'tag':
    destroyTag(instanceId, mediaId);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const instanceClip = (type, entityId, instanceId) => {
  switch (type) {
  case 'tag':
    console.warn(`TODO instance ${instanceId} -> clip`);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const playlistLaunch = (type, data) => {
  switch (type) {
  case 'tags':
    console.warn('TODO playlist tags', data);
    break;
  default:
    console.error(`${type} not handled`);
  }
};

const commentThreadCreate = (time, text, mediaId, parentID, callback) => {
  createCommentThread(text, `t=${time}`, `${mediaId}`, parentID, callback);
};

const commentThreadDelete = (id, annotated_id, callback) => {
  destroyComment(id, annotated_id, callback);
};

const commentCreate = (id, text, parentID, callback) => {
  console.log({
    id, text, parentID, callback,
  });
  createComment(text, null, `${id}`, parentID, callback);
};

const commentEdit = (threadId, commentId, text, callback) => {
  console.log({
    threadId, commentId, text, callback,
  });
  updateComment(commentId, text, callback);
};

const commentDelete = (threadId, commentId) => {
  destroyComment(commentId, threadId);
};

class MediaTimeline extends Component {
  render() {
    const {
      media: {
        id: mediaId, dbid,
        tags: { edges = [] },
        comments,
      },
      duration, time, setPlayerState,
      currentUser: { name: first_name, id: userId, profile_image: profile_img_url },
    } = this.props;

    const projecttags = [];
    const entities = {};

    edges
      .filter(({ node }) => !!node)
      .forEach(({ node: { id: instance, fragment, tag_text_object } }) => {
        if (!tag_text_object) return;

        const { id, text: name } = tag_text_object;
        if (!fragment) {
          projecttags.push({ id, name });
          return;
        }

        if (!entities[id]) {
          entities[id] = {
            id, name, project_tag: { id, name }, instances: [],
          };
        }

        const { t = '0,0', type = 'tag' } = qs.parse(fragment);
        const [start_seconds, end_seconds] = t.split(',').map(n => parseFloat(n));

        entities[id].type = type;
        entities[id].instances.push({ start_seconds, end_seconds, id: instance });
      });

    const commentThreads = comments.edges.map(({
      node: {
        id: thread_id, dbid, text, parsed_fragment,
        annotator: {
          id: annoId, name, profile_image,
        },
        comments,
      },
    }) => ({
      id: thread_id,
      dbid,
      text,
      start_seconds: parsed_fragment.t[0],
      user: {
        id: annoId,
        first_name: name,
        last_name: '',
        profile_img_url: profile_image,
      },
      replies: comments && comments.edges ? comments.edges.map(({
        node: {
          id, created_at, text,
          annotator: {
            id: annoId, name, profile_image,
          },
        },
      }) => ({
        id,
        thread_id,
        created_at,
        text,
        start_seconds: 0,
        user: {
          id: annoId,
          first_name: name,
          last_name: '',
          profile_img_url: profile_image,
        },
      })).sort((a, b) => a.created_at - b.created_at) : [],
    }));

    const data = {
      commentThreads,
      project: {
        projectclips: [],
        projectplaces: [],
        projecttags,
      },
      videoClips: [],
      videoPlaces: [],
      videoTags: Object.values(entities).filter(({ type }) => type === 'tag' || type === 'tags'),
      user: {
        first_name,
        id: userId,
        last_name: '',
        profile_img_url,
      },
    };

    return (
      <Timeline
        currentTime={time}
        data={data}
        duration={duration}
        onBeforeCommentThreadCreate={(a, b, c, d, e) => console.log(a, b, c, d, e)}
        onCommentCreate={(threadId, text, callback) =>
          commentCreate(commentThreads.find(({ id }) =>
            id === threadId).dbid, text, threadId, callback)}
        onCommentDelete={(threadId, commentId) => commentDelete(threadId, commentId)}
        onCommentEdit={(threadId, commentId, text, callback) =>
          commentEdit(threadId, commentId, text, callback)}
        onCommentThreadCreate={
          (t, text, callback) => commentThreadCreate(t, text, dbid, mediaId, callback)
        }
        onCommentThreadDelete={(id, callback) => commentThreadDelete(id, mediaId, callback)}
        onEntityCreate={
          (type, payload, callback) => entityCreate(type, payload, dbid, mediaId, callback)
        }
        onEntityDelete={
          (type, entityId, callback) =>
            entityDelete(type, entityId, data.videoTags.find(({ id }) =>
              entityId === id).instances, dbid, callback)
        }
        onEntityUpdate={
          (type, entityId, payload, callback) => entityUpdate(type, entityId, payload, callback)
        }
        onInstanceClip={(type, entityId, instanceId) => instanceClip(type, entityId, instanceId)}
        onInstanceCreate={
          (type, id, payload, callback) =>
            instanceCreate(type, id, data.videoTags.find(({ id: _id }) =>
              _id === id).name, payload, dbid, mediaId, callback)
        }
        onInstanceDelete={(type, entityId, instanceId) => instanceDelete(type, instanceId, dbid)}
        onInstanceUpdate={(type, entityId, instanceId, { start_seconds, end_seconds }) => instanceUpdate(type, entityId, instanceId, `t=${start_seconds},${end_seconds}&type=${type}`)}
        onPlaylistLaunch={type => playlistLaunch(type, data)}
        onTimeChange={seekTo => setPlayerState({ seekTo })}
      />
    );
  }
}

export default MediaTimeline;
<|endoftext|>


<commit_before>/*---------------------------------------------------------------------------*\
 |  About.js                                                                 |
 |                                                                           |
 |  Copyright © 2017-2020, Rajiv Bakulesh Shah, original author.             |
 |                                                                           |
 |      This program is free software: you can redistribute it and/or modify |
 |      it under the terms of the GNU General Public License as published by |
 |      the Free Software Foundation, either version 3 of the License, or    |
 |      (at your option) any later version.                                  |
 |                                                                           |
 |      This program is distributed in the hope that it will be useful, but  |
 |      WITHOUT ANY WARRANTY; without even the implied warranty of           |
 |      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
 |      General Public License for more details.                             |
 |                                                                           |
 |      You should have received a copy of the GNU General Public License    |
 |      along with this program.  If not, see:                               |
 |          <http://www.gnu.org/licenses/>                                   |
\*---------------------------------------------------------------------------*/

import React, { useEffect } from 'react';
import { Link, withRouter } from 'react-router-dom';
import Player from './Player';
import './About.css';

const About = React.memo(function About(props) {
  useEffect(() => {
    document.title = 'Spool - About Me';
  });

  return (
    <>
      <Player state='background' history={props.history} />

About Me

What is Spool?

          <Link to='/'>Spool</Link> takes the experience of channel surfing and
          puts it online.  I hope that you enjoy using it as much as I’ve
          enjoyed building it.

Just … Why?

          I want to build something frivolous, but in a particular way.
          Inexplicable like MTV or Snapchat.

How can I contact you?

          Find me on Reddit
          (u/Brainix),
          email me
          (brainix@gmail.com),
          or have coffee with me any time in San Francisco.

    </>
  );
});

export default withRouter(About);
<commit_msg>Break /wtf page to verify Sentry setup<commit_after>/*---------------------------------------------------------------------------*\
 |  About.js                                                                 |
 |                                                                           |
 |  Copyright © 2017-2020, Rajiv Bakulesh Shah, original author.             |
 |                                                                           |
 |      This program is free software: you can redistribute it and/or modify |
 |      it under the terms of the GNU General Public License as published by |
 |      the Free Software Foundation, either version 3 of the License, or    |
 |      (at your option) any later version.                                  |
 |                                                                           |
 |      This program is distributed in the hope that it will be useful, but  |
 |      WITHOUT ANY WARRANTY; without even the implied warranty of           |
 |      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
 |      General Public License for more details.                             |
 |                                                                           |
 |      You should have received a copy of the GNU General Public License    |
 |      along with this program.  If not, see:                               |
 |          <http://www.gnu.org/licenses/>                                   |
\*---------------------------------------------------------------------------*/

import React, { useEffect } from 'react';
import { Link, withRouter } from 'react-router-dom';
import Player from './Player';
import './About.css';

const About = React.memo(function About(props) {
  useEffect(() => {
    document.title = 'Spool - About Me';
  });

  return (
    <>
      <Player state='background' history={props.history} />

About Me

What is Spool?

          <Link to='/'>Spool</Link> takes the experience of channel surfing and
          puts it online.  I hope that you enjoy using it as much as I’ve
          enjoyed building it.

Just … Why?

          I want to build something frivolous, but in a particular way.
          Inexplicable like MTV or Snapchat.

How can I contact you?

          Find me on Reddit
          (u/Brainix),
          email me
          (brainix@gmail.com),
          or have coffee with me any time in San Francisco.

        // Temporary to verify Sentry setup:
        <button onClick={methodDoesNotExist}>Break the world</button>

    </>
  );
});

export default withRouter(About);
<|endoftext|>


<commit_before>/**
 * Flatten Nested List Iterator
 *
 * Given a nested list of integers, implement an iterator to flatten it.
 *
 * Each element is either an integer, or a list -- whose elements may also be integers or other lists.
 *
 * Example 1:
 * Given the list [[1,1],2,[1,1]],
 *
 * By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1].
 *
 * Example 2:
 * Given the list [1,[4,[6]]],
 *
 * By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,4,6].
 */

/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * function NestedInteger() {
 *
 *     Return true if this NestedInteger holds a single integer, rather than a nested list.
 *     @return {boolean}
 *     this.isInteger = function() {
 *         ...
 *     };
 *
 *     Return the single integer that this NestedInteger holds, if it holds a single integer
 *     Return null if this NestedInteger holds a nested list
 *     @return {integer}
 *     this.getInteger = function() {
 *         ...
 *     };
 *
 *     Return the nested list that this NestedInteger holds, if it holds a nested list
 *     Return null if this NestedInteger holds a single integer
 *     @return {NestedInteger[]}
 *     this.getList = function() {
 *         ...
 *     };
 * };
 */

/**
 * @constructor
 * @param {NestedInteger[]} nestedList
 */
class NestedIterator {
  constructor(nestedList) {
    this.stack = nestedList.reverse();
  }

  /**
   * @this NestedIterator
   * @returns {boolean}
   */
  hasNext() {
    while (this.stack.length > 0) {
      if (!isNaN(this.stack[this.stack.length - 1])) {
        return true;
      }

      const listItem = this.stack.pop();

      listItem.forEach(element => {
        this.stack.push(element);
      });
    }
    return false;
  }

  /**
   * @this NestedIterator
   * @returns {integer}
   */
  next() {
    return this.stack.pop();
  }
}

/**
 * Your NestedIterator will be called like this:
 * var i = new NestedIterator(nestedList), a = [];
 * while (i.hasNext()) a.push(i.next());
 */

export { NestedIterator };
<commit_msg>Update flatten-nested-list-iterator.js<commit_after>/**
 * Flatten Nested List Iterator
 *
 * Given a nested list of integers, implement an iterator to flatten it.
 *
 * Each element is either an integer, or a list -- whose elements may also be integers or other lists.
 *
 * Example 1:
 * Given the list [[1,1],2,[1,1]],
 *
 * By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1].
 *
 * Example 2:
 * Given the list [1,[4,[6]]],
 *
 * By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,4,6].
 */

/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * function NestedInteger() {
 *
 *     Return true if this NestedInteger holds a single integer, rather than a nested list.
 *     @return {boolean}
 *     this.isInteger = function() {
 *         ...
 *     };
 *
 *     Return the single integer that this NestedInteger holds, if it holds a single integer
 *     Return null if this NestedInteger holds a nested list
 *     @return {integer}
 *     this.getInteger = function() {
 *         ...
 *     };
 *
 *     Return the nested list that this NestedInteger holds, if it holds a nested list
 *     Return null if this NestedInteger holds a single integer
 *     @return {NestedInteger[]}
 *     this.getList = function() {
 *         ...
 *     };
 * };
 */

/**
 * @constructor
 * @param {NestedInteger[]} nestedList
 */
class NestedIterator {
  constructor(nestedList) {
    this.stack = nestedList.reverse();
  }

  /**
   * @this NestedIterator
   * @returns {boolean}
   */
  hasNext() {
    while (this.stack.length > 0) {
      if (!isNaN(this.stack[this.stack.length - 1])) {
        return true;
      }

      const listItem = this.stack.pop().reverse();

      listItem.forEach(element => {
        this.stack.push(element);
      });
    }
    return false;
  }

  /**
   * @this NestedIterator
   * @returns {integer}
   */
  next() {
    return this.stack.pop();
  }
}

/**
 * Your NestedIterator will be called like this:
 * var i = new NestedIterator(nestedList), a = [];
 * while (i.hasNext()) a.push(i.next());
 */

export { NestedIterator };
<|endoftext|>


<commit_before>(function ($) {
    $.fn.multipleInput = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.multipleInput');
            return false;
        }
    };

    var defaultOptions = {
        id: null,
        template: null,
        btn_action: null,
        btn_type: null,
        limit: 1,
        replacement: []
    };

    var methods = {
        init: function (options) {
            var settings = $.extend({}, defaultOptions, options || {}),
                wrapper = $('#' + settings.id),
                form = wrapper.closest('form'),
                id = this.selector.replace('#', '');

            wrapper.data('multipleInput', {
                settings: settings,
                currentIndex: 0,
                attributeDefaults: {}
            });

            $(document).on('click.multipleInput', '#' + settings.id + ' .js-input-remove', function (e) {
                e.preventDefault();
                methods.removeInput.apply(this);
            });

            $(document).on('click.multipleInput', '#' + settings.id + ' .js-input-plus', function (e) {
                e.preventDefault();
                methods.addInput.apply(this);
            });

            var intervalID = setInterval(function(){
                if (typeof form.data('yiiActiveForm') === 'object') {
                    var attribute = form.yiiActiveForm('find', id);
                    if (typeof attribute === 'object') {
                        $.each(attribute, function (key, value) {
                            if (['id', 'input', 'container'].indexOf(key) == -1) {
                                wrapper.data('multipleInput').attributeDefaults[key] = value;
                            }
                        });
                        form.yiiActiveForm('remove', id);
                    }

                    wrapper.find('.multiple-input-list').find('input, select, textarea').each(function () {
                        methods.addAttribute.apply(this);
                    });
                    wrapper.data('multipleInput').currentIndex = wrapper.find('.multiple-input-list__item').length;
                    clearInterval(intervalID);
                }
            }, 100);

        },

        addInput: function () {
            var wrapper     = $(this).closest('.multiple-input').first(),
                data        = wrapper.data('multipleInput'),
                settings    = data.settings,
                template    = settings.template,
                inputList   = wrapper.find('.multiple-input-list').first(),
                count       = wrapper.find('.multiple-input-list__item').length,
                replacement = settings.replacement || [];

            if (settings.limit != null && count >= settings.limit) {
                return;
            }
            var search = ['{index}', '{btn_action}', '{btn_type}', '{value}'],
                replace = [data.currentIndex, settings.btn_action, settings.btn_type, ''];

            for (var i in search) {
                template = template.replaceAll(search[i], replace[i]);
            }

            for (var j in replacement) {
                template = template.replaceAll('{' + j + '}', replacement[j]);
            }

            $(template).hide().appendTo(inputList).fadeIn(300);
            $(template).find('input, select, textarea').each(function () {
                methods.addAttribute.apply(this);
            });

            wrapper.data('multipleInput').currentIndex++;
        },

        removeInput: function () {
            var line = $(this).closest('.multiple-input-list__item');
            line.find('input, select, textarea').each(function () {
                methods.removeAttribute.apply(this);
            });
            line.fadeOut(300, function () {
                $(this).remove();
            });
        },

        addAttribute: function () {
            var id = $(this).attr('id'),
                ele = $('#' + $(this).attr('id')),
                wrapper = ele.closest('.multiple-input').first(),
                form = ele.closest('form');

            form.yiiActiveForm('add', $.extend(wrapper.data('multipleInput').attributeDefaults, {
                'id': id,
                'input': '#' + id,
                'container': '.field-' + id
            }));
        },

        removeAttribute: function () {
            var id = $(this).attr('id');
            var form = $('#' + $(this).attr('id')).closest('form');
            form.yiiActiveForm('remove', id);
        }

    };

    String.prototype.replaceAll = function(search, replace){
        return this.split(search).join(replace);
    };
})(window.jQuery);<commit_msg>add js events<commit_after>(function ($) {
    $.fn.multipleInput = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.multipleInput');
            return false;
        }
    };

    var defaultOptions = {
        id: null,
        template: null,
        btn_action: null,
        btn_type: null,
        limit: 1,
        replacement: []
    };

    var methods = {
        init: function (options) {
            var settings = $.extend({}, defaultOptions, options || {}),
                wrapper = $('#' + settings.id),
                form = wrapper.closest('form'),
                id = this.selector.replace('#', '');

            wrapper.data('multipleInput', {
                settings: settings,
                currentIndex: 0,
                attributeDefaults: {}
            });

            $(document).on('click.multipleInput', '#' + settings.id + ' .js-input-remove', function (e) {
                e.preventDefault();
                methods.removeInput.apply(this);
            });

            $(document).on('click.multipleInput', '#' + settings.id + ' .js-input-plus', function (e) {
                e.preventDefault();
                methods.addInput.apply(this);
            });

            var intervalID = setInterval(function(){
                if (typeof form.data('yiiActiveForm') === 'object') {
                    var attribute = form.yiiActiveForm('find', id);
                    if (typeof attribute === 'object') {
                        $.each(attribute, function (key, value) {
                            if (['id', 'input', 'container'].indexOf(key) == -1) {
                                wrapper.data('multipleInput').attributeDefaults[key] = value;
                            }
                        });
                        form.yiiActiveForm('remove', id);
                    }

                    wrapper.find('.multiple-input-list').find('input, select, textarea').each(function () {
                        methods.addAttribute.apply(this);
                    });
                    wrapper.data('multipleInput').currentIndex = wrapper.find('.multiple-input-list__item').length;
                    clearInterval(intervalID);
                }
            }, 100);
            wrapper.trigger('init');

        },

        addInput: function () {
            var wrapper     = $(this).closest('.multiple-input').first(),
                data        = wrapper.data('multipleInput'),
                settings    = data.settings,
                template    = settings.template,
                inputList   = wrapper.find('.multiple-input-list').first(),
                count       = wrapper.find('.multiple-input-list__item').length,
                replacement = settings.replacement || [];

            if (settings.limit != null && count >= settings.limit) {
                return;
            }
            var search = ['{index}', '{btn_action}', '{btn_type}', '{value}'],
                replace = [data.currentIndex, settings.btn_action, settings.btn_type, ''];

            for (var i in search) {
                template = template.replaceAll(search[i], replace[i]);
            }

            for (var j in replacement) {
                template = template.replaceAll('{' + j + '}', replacement[j]);
            }

            $(template).hide().appendTo(inputList).fadeIn(300);
            $(template).find('input, select, textarea').each(function () {
                methods.addAttribute.apply(this);
            });

            wrapper.data('multipleInput').currentIndex++;
            wrapper.trigger('addNewRow');
        },

        removeInput: function () {
            var wrapper = $(this).closest('.multiple-input').first(),
                line = $(this).closest('.multiple-input-list__item');
            line.find('input, select, textarea').each(function () {
                methods.removeAttribute.apply(this);
            });
            line.fadeOut(300, function () {
                $(this).remove();
            });
            wrapper.trigger('removeRow');
        },

        addAttribute: function () {
            var id = $(this).attr('id'),
                ele = $('#' + $(this).attr('id')),
                wrapper = ele.closest('.multiple-input').first(),
                form = ele.closest('form');

            form.yiiActiveForm('add', $.extend(wrapper.data('multipleInput').attributeDefaults, {
                'id': id,
                'input': '#' + id,
                'container': '.field-' + id
            }));
        },

        removeAttribute: function () {
            var id = $(this).attr('id');
            var form = $('#' + $(this).attr('id')).closest('form');
            form.yiiActiveForm('remove', id);
        }

    };

    String.prototype.replaceAll = function(search, replace){
        return this.split(search).join(replace);
    };
})(window.jQuery);<|endoftext|>


<commit_before>import MJMLColumnElement from './decorators/MJMLColumnElement'
import React, { Component } from 'react'
import _ from 'lodash'

/**
 * Displays an image to your email. It is mostly similar to the HTML img tag
 */
@MJMLColumnElement({
  tagName: 'mj-image',
  attributes: {
    'height': 'auto',
    'padding-bottom': '10px',
    'padding-left': '25px',
    'padding-right': '25px',
    'padding-top': '10px',
    'align': 'center',
    'alt': '',
    'border': 'none',
    'href': '',
    'src': ''
  }
})
class Image extends Component {

  static baseStyles = {
    table: {
      borderCollapse: 'collapse',
      borderSpacing: '0'
    },
    img: {
      border: 'none',
      display: 'block',
      outline: 'none',
      textDecoration: 'none'
    }
  };

  getContentWidth() {
    const { mjAttribute } = this.props
    const width = _.min([parseInt(mjAttribute('width')), parseInt(mjAttribute('parentWidth'))])
    const paddingRight = parseInt(mjAttribute('padding-left')) || 0
    const paddingLeft = parseInt(mjAttribute('padding-right')) || 0
    const widthOverflow = paddingLeft + paddingRight + width - parseInt(mjAttribute('parentWidth'))

    return widthOverflow > 0 ? width - widthOverflow : width
  }

  getStyles() {
    const { mjAttribute } = this.props

    return _.merge({}, this.constructor.baseStyles, {
      img: {
        border: mjAttribute('border'),
        maxWidth: this.getContentWidth(),
        height: mjAttribute('height')
      }
    })
  }

  renderImage() {
    const { mjAttribute } = this.props

    const img = (

    )

    if (mjAttribute('href') != '') {
      return (

          {img}

      )
    }
    else {
      return img
    }
  }

  render() {
    const { mjAttribute } = this.props

    this.styles = this.getStyles()

    return (

              {this.renderImage()}

    )
  }

}

export default Image
<commit_msg>Should fix #39<commit_after>import MJMLColumnElement from './decorators/MJMLColumnElement'
import React, { Component } from 'react'
import _ from 'lodash'

/**
 * Displays an image to your email. It is mostly similar to the HTML img tag
 */
@MJMLColumnElement({
  tagName: 'mj-image',
  attributes: {
    'height': 'auto',
    'padding-bottom': '10px',
    'padding-left': '25px',
    'padding-right': '25px',
    'padding-top': '10px',
    'align': 'center',
    'alt': '',
    'border': 'none',
    'href': '',
    'src': ''
  }
})
class Image extends Component {

  static baseStyles = {
    table: {
      borderCollapse: 'collapse',
      borderSpacing: '0'
    },
    img: {
      border: 'none',
      display: 'block',
      outline: 'none',
      textDecoration: 'none',
      width: "100%"
    }
  };

  getContentWidth() {
    const { mjAttribute } = this.props
    const width = _.min([parseInt(mjAttribute('width')), parseInt(mjAttribute('parentWidth'))])
    const paddingRight = parseInt(mjAttribute('padding-left')) || 0
    const paddingLeft = parseInt(mjAttribute('padding-right')) || 0
    const widthOverflow = paddingLeft + paddingRight + width - parseInt(mjAttribute('parentWidth'))

    return widthOverflow > 0 ? width - widthOverflow : width
  }

  getStyles() {
    const { mjAttribute } = this.props

    return _.merge({}, this.constructor.baseStyles, {
      img: {
        border: mjAttribute('border'),
        maxWidth: this.getContentWidth(),
        height: mjAttribute('height')
      }
    })
  }

  renderImage() {
    const { mjAttribute } = this.props

    const img = (

    )

    if (mjAttribute('href') != '') {
      return (

          {img}

      )
    }
    else {
      return img
    }
  }

  render() {
    const { mjAttribute } = this.props

    this.styles = this.getStyles()

    return (

              {this.renderImage()}

    )
  }

}

export default Image
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import '../assets/stylesheets/Input.scss';
import Pokemon from '../assets/data/Pokemon.json';
import Dust from '../assets/data/Dust.json';

// stackoverflow.com/questions/10834796/validate-that-a-string-is-a-positive-integer
function validateNumericEntry(number) {
  return number >>> 0 === parseFloat(number);
}

function validateDustEntry(number) {
  const dustData = Dust.find((dust) =>
    (dust.cost === Number(number)));
  return dustData !== null && dustData !== undefined;
}

function getValidityIcon(item) {
  if (item) {
    return ;
  }

  return ;
}

class Input extends Component {
  static propTypes = {
    onValidInputCB: PropTypes.func.isRequired,
  }

  constructor(props) {
    super(props);

    this.state = {
      name: '',
      found: false,
      pokemon: {},
      validCP: false,
      validHP: false,
      validDust: false,
      wild: true,
      trained: false,
    };
    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
    this.onCPChange = this.onCPChange.bind(this);
    this.onHPChange = this.onHPChange.bind(this);
    this.onDustChange = this.onDustChange.bind(this);
    this.checkValidInput = this.checkValidInput.bind(this);
    this.onWildChange = this.onWildChange.bind(this);
    this.onTrainedChange = this.onTrainedChange.bind(this);
  }

  onSubmit(e) {
    e.preventDefault();
  }

  onChange(e) {
    let pkmn = Pokemon.find((pokemon) =>
      (pokemon.name.toLowerCase() === e.target.value.toLowerCase()));
    const found = pkmn !== null && pkmn !== undefined;

    if (!found) {
      pkmn = {};
    }

    this.checkValidInput({ ...this.state,
      name: e.target.value, found, pokemon: pkmn });

    this.setState({
      name: e.target.value,
      found,
      pokemon: pkmn,
    });
  }

  onCPChange(e) {
    this.checkValidInput({ ...this.state,
      CP: e.target.value, validCP: validateNumericEntry(e.target.value) });

    this.setState({
      CP: e.target.value,
      validCP: validateNumericEntry(e.target.value),
    });
  }

  onHPChange(e) {
    this.checkValidInput({ ...this.state,
      HP: e.target.value, validHP: validateNumericEntry(e.target.value) });

    this.setState({
      HP: e.target.value,
      validHP: validateNumericEntry(e.target.value),
    });
  }

  onDustChange(e) {
    this.checkValidInput({ ...this.state,
      dust: e.target.value, validDust: validateDustEntry(e.target.value) });

    this.setState({
      dust: e.target.value,
      validDust: validateDustEntry(e.target.value),
    });
  }

  onWildChange(e) {
    this.checkValidInput({ ...this.state,
      wild: e.target.checked });

    this.setState({
      wild: e.target.checked,
    });
  }

  onTrainedChange(e) {
    this.checkValidInput({ ...this.state,
      trained: e.target.checked });

    this.setState({
      trained: e.target.checked,
    });
  }

  checkValidInput(newState) {
    const { found, validCP, validHP,
      validDust, pokemon, CP, HP, dust, wild, trained } = newState;
    const { onValidInputCB } = this.props;

    if (found && validCP && validHP && validDust) {
      onValidInputCB(pokemon, Number(CP), Number(HP), Number(dust), wild, trained);
    }
  }

  render() {
    let nameStatus = getValidityIcon(this.state.found);
    let cpStatus = getValidityIcon(this.state.validCP);
    let hpStatus = getValidityIcon(this.state.validHP);
    let dustStatus = getValidityIcon(this.state.validDust);

    return (

            <form onSubmit={this.onSubmit}>

                    name
                    <input onChange={this.onChange} className="form-input input-lg"></input>
                    {nameStatus}

                    cp
                    <input onChange={this.onCPChange} className="form-input input-lg"></input>
                    {cpStatus}

                    hp
                    <input onChange={this.onHPChange} className="form-input input-lg"></input>
                    {hpStatus}

                    <input onChange={this.onDustChange} className="form-input input-lg"></input>
                    {dustStatus}

                    <input onChange={this.onWildChange} type="checkbox"
                      checked={this.state.wild}></input>
                     caught in wild?

                  <input onChange={this.onTrainedChange} type="checkbox"
                    checked={this.state.trained}></input>
                   powered up?

            </form>

    );
  }
}

export default Input;
<commit_msg>fix checkbox alignment<commit_after>import React, { Component, PropTypes } from 'react';
import '../assets/stylesheets/Input.scss';
import Pokemon from '../assets/data/Pokemon.json';
import Dust from '../assets/data/Dust.json';

// stackoverflow.com/questions/10834796/validate-that-a-string-is-a-positive-integer
function validateNumericEntry(number) {
  return number >>> 0 === parseFloat(number);
}

function validateDustEntry(number) {
  const dustData = Dust.find((dust) =>
    (dust.cost === Number(number)));
  return dustData !== null && dustData !== undefined;
}

function getValidityIcon(item) {
  if (item) {
    return ;
  }

  return ;
}

class Input extends Component {
  static propTypes = {
    onValidInputCB: PropTypes.func.isRequired,
  }

  constructor(props) {
    super(props);

    this.state = {
      name: '',
      found: false,
      pokemon: {},
      validCP: false,
      validHP: false,
      validDust: false,
      wild: true,
      trained: false,
    };
    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
    this.onCPChange = this.onCPChange.bind(this);
    this.onHPChange = this.onHPChange.bind(this);
    this.onDustChange = this.onDustChange.bind(this);
    this.checkValidInput = this.checkValidInput.bind(this);
    this.onWildChange = this.onWildChange.bind(this);
    this.onTrainedChange = this.onTrainedChange.bind(this);
  }

  onSubmit(e) {
    e.preventDefault();
  }

  onChange(e) {
    let pkmn = Pokemon.find((pokemon) =>
      (pokemon.name.toLowerCase() === e.target.value.toLowerCase()));
    const found = pkmn !== null && pkmn !== undefined;

    if (!found) {
      pkmn = {};
    }

    this.checkValidInput({ ...this.state,
      name: e.target.value, found, pokemon: pkmn });

    this.setState({
      name: e.target.value,
      found,
      pokemon: pkmn,
    });
  }

  onCPChange(e) {
    this.checkValidInput({ ...this.state,
      CP: e.target.value, validCP: validateNumericEntry(e.target.value) });

    this.setState({
      CP: e.target.value,
      validCP: validateNumericEntry(e.target.value),
    });
  }

  onHPChange(e) {
    this.checkValidInput({ ...this.state,
      HP: e.target.value, validHP: validateNumericEntry(e.target.value) });

    this.setState({
      HP: e.target.value,
      validHP: validateNumericEntry(e.target.value),
    });
  }

  onDustChange(e) {
    this.checkValidInput({ ...this.state,
      dust: e.target.value, validDust: validateDustEntry(e.target.value) });

    this.setState({
      dust: e.target.value,
      validDust: validateDustEntry(e.target.value),
    });
  }

  onWildChange(e) {
    this.checkValidInput({ ...this.state,
      wild: e.target.checked });

    this.setState({
      wild: e.target.checked,
    });
  }

  onTrainedChange(e) {
    this.checkValidInput({ ...this.state,
      trained: e.target.checked });

    this.setState({
      trained: e.target.checked,
    });
  }

  checkValidInput(newState) {
    const { found, validCP, validHP,
      validDust, pokemon, CP, HP, dust, wild, trained } = newState;
    const { onValidInputCB } = this.props;

    if (found && validCP && validHP && validDust) {
      onValidInputCB(pokemon, Number(CP), Number(HP), Number(dust), wild, trained);
    }
  }

  render() {
    let nameStatus = getValidityIcon(this.state.found);
    let cpStatus = getValidityIcon(this.state.validCP);
    let hpStatus = getValidityIcon(this.state.validHP);
    let dustStatus = getValidityIcon(this.state.validDust);

    return (

            <form onSubmit={this.onSubmit}>

                    name
                    <input onChange={this.onChange} className="form-input input-lg"></input>
                    {nameStatus}

                    cp
                    <input onChange={this.onCPChange} className="form-input input-lg"></input>
                    {cpStatus}

                    hp
                    <input onChange={this.onHPChange} className="form-input input-lg"></input>
                    {hpStatus}

                    <input onChange={this.onDustChange} className="form-input input-lg"></input>
                    {dustStatus}

                    <input onChange={this.onWildChange} type="checkbox"
                      checked={this.state.wild}></input>
                     caught in wild?

                  <input onChange={this.onTrainedChange} type="checkbox"
                    checked={this.state.trained}></input>
                   powered up?

            </form>

    );
  }
}

export default Input;
<|endoftext|>


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

const Input = (props) => {
  return (
      <input
        maxlength={props.maxlength}
        size={props.size}
        placeholder={props.placeholder}
        type={props.type}
        pattern={props.pattern}
        title={props.title}
      />
    )
}

Input.propTypes = {
  maxlength: PropTypes.string,
  size: PropTypes.string,
  placeholder: PropTypes.string,
  type: PropTypes.string,
  pattern: PropTypes.string,
  title: PropTypes.string
};

export default Input;<commit_msg>fix some spacing issues<commit_after>import React from 'react';
import PropTypes from 'prop-types';

const Input = (props) => {
  return (
    <input
      maxLength={props.maxlength}
      size={props.size}
      placeholder={props.placeholder}
      type={props.type}
      pattern={props.pattern}
      title={props.title}
    />
  );
};

Input.propTypes = {
  maxlength: PropTypes.string,
  size: PropTypes.string,
  placeholder: PropTypes.string,
  type: PropTypes.string,
  pattern: PropTypes.string,
  title: PropTypes.string
};

export default Input;<|endoftext|>


<commit_before>var _ = require('underscore');
var utils = require('./controllerUtils.js');
var events = require('../../events');
var msg = require('./msg');
var model = require('./model');

function passback(id, result, nextFunction){
  // filter out results here
  events.Emitter.emit(id, result);
}

function subRegister(dataModel, method, params, data, ip, id) {
  // Provide some basic sanity checks
  if(!data.email) return passback(id, msg.missingEmail());
  if(utils.invalidEmail(data.email)) return passback(id, msg.badEmail(data.email));
  if(!data.password) return passback(id, msg.missingPwd());
  if(utils.invalidPassword(data.password) ) return passback(id, msg.badPwd());

  dataModel.subscriber.create(data.email, data.password, ip, function(result){
      passback(id, result);
  });
}

function subVerify(dataModel, method, params, data, ip, id) {

  var token = params[1];
  // Ensure a verification token is present and valid
  if(!token) return passback(id, msg.missingVerificationToken());
  // if(!validToken(params[1])) return passback(id, msg.missingToken());

  dataModel.subscriber.verify(token, function(result){
      passback(id, result);
  });

}

function subReset(dataModel, method, params, data, ip, id) {
  // Ensure email is present and valid
  if(!data.email) return msg.missingEmail();
  //if(badEmail(data.email)) return msg.badEmail(data.email);
  // Return the result of password reset
  dataModel.subscriber.reset(data.email, function(result){
    passback(id, result);
  })
}

function subLogin(dataModel, method, params, data, ip, id) {
  if(!data.email) return msg.missingEmail();
 // if(badEmail(data.email)) return msg.badEmail(data.email);
  dataModel.session.authenticate(data.email, data.password,
  function(result){
    passback(id, result);
  });
}

function subLogout(dataModel, method, params, data, ip, id) {
  return msg.success(); // TODO: implement this..
}

function sessAuthenticate(dataModel, headers, cb) {
  if(headers['x-access-token']) {
    dataModel.session.getByAccessToken(headers['x-access-token'],
    function(result){ cb(result); });
  } else cb(null);
}

module.exports = function(testAdapter) {
  var dataModel;
	if(testAdapter){
		dataModel = model(testAdapter);
  } else {
		dataModel = model();
	}
  return {
    authenticate: _.bind(sessAuthenticate, null, dataModel),
    module: {
      noauth: {
        register: _.bind(subRegister, null, dataModel),
        verify: _.bind(subVerify, null, dataModel),
        reset: _.bind(subRegister, null, dataModel),
        login: _.bind(subLogin, null, dataModel)
      },
      auth: {
        logout: _.bind(subLogout, null, dataModel)
      }
    }
  }
}

<commit_msg>Update controller.js<commit_after>var _ = require('underscore');
var utils = require('./controllerUtils.js');
var events = require('../../events');
var msg = require('./msg');
var model = require('./model');

function passback(id, result, nextFunction){
  // filter out results here
  events.Emitter.emit(id, result);
}

function subRegister(dataModel, method, params, data, ip, id) {
  // Provide some basic sanity checks
  if(!data.email) return passback(id, msg.missingEmail());
  if(utils.invalidEmail(data.email)) return passback(id, msg.badEmail(data.email));
  if(!data.password) return passback(id, msg.missingPwd());
  if(utils.invalidPassword(data.password) ) return passback(id, msg.badPwd());

  dataModel.subscriber.create(data.email, data.password, ip, function(result){
      passback(id, result);
  });
}

function subVerify(dataModel, method, params, data, ip, id) {

  var token = params[1];
  // Ensure a verification token is present and valid
  if(!token) return passback(id, msg.missingVerificationToken());
  if(invalidToken(token)) return passback(id, msg.missingToken());

  dataModel.subscriber.verify(token, function(result){
      passback(id, result);
  });

}

function subReset(dataModel, method, params, data, ip, id) {
  // Ensure email is present and valid
  if(!data.email) return msg.missingEmail();
  //if(badEmail(data.email)) return msg.badEmail(data.email);
  // Return the result of password reset
  dataModel.subscriber.reset(data.email, function(result){
    passback(id, result);
  })
}

function subLogin(dataModel, method, params, data, ip, id) {
  if(!data.email) return msg.missingEmail();
 // if(badEmail(data.email)) return msg.badEmail(data.email);
  dataModel.session.authenticate(data.email, data.password,
  function(result){
    passback(id, result);
  });
}

function subLogout(dataModel, method, params, data, ip, id) {
  return msg.success(); // TODO: implement this..
}

function sessAuthenticate(dataModel, headers, cb) {
  if(headers['x-access-token']) {
    dataModel.session.getByAccessToken(headers['x-access-token'],
    function(result){ cb(result); });
  } else cb(null);
}

module.exports = function(testAdapter) {
  var dataModel;
	if(testAdapter){
		dataModel = model(testAdapter);
  } else {
		dataModel = model();
	}
  return {
    authenticate: _.bind(sessAuthenticate, null, dataModel),
    module: {
      noauth: {
        register: _.bind(subRegister, null, dataModel),
        verify: _.bind(subVerify, null, dataModel),
        reset: _.bind(subRegister, null, dataModel),
        login: _.bind(subLogin, null, dataModel)
      },
      auth: {
        logout: _.bind(subLogout, null, dataModel)
      }
    }
  }
}

<|endoftext|>


<commit_before>/* eslint-disable */
Blockly.WorkspaceAudio.prototype.preload = function() {};
Blockly.FieldDropdown.prototype.render_ = function() {
    if (!this.visible_) {
        this.size_.width = 0;
        return;
    }
    if (this.sourceBlock_ && this.arrow_) {
        // Update arrow's colour.
        this.arrow_.style.fill = this.sourceBlock_.getColour();
    }
    goog.dom.removeChildren(this.textElement_);
    goog.dom.removeNode(this.imageElement_);
    this.imageElement_ = null;

    if (this.imageJson_) {
        this.renderSelectedImage_();
    } else {
        this.renderSelectedText_();
    }
    this.borderRect_.setAttribute('height', this.size_.height - 8);
    this.borderRect_.setAttribute('width', this.size_.width + Blockly.BlockSvg.SEP_SPACE_X);
};
Blockly.FieldDropdown.prototype.renderSelectedText_ = function() {
    // Text option is selected.
    // Replace the text.
    const textNode = document.createTextNode(this.getDisplayText_());
    this.textElement_.appendChild(textNode);
    // Insert dropdown arrow.
    if (this.sourceBlock_.RTL) {
        this.textElement_.insertBefore(this.arrow_, this.textElement_.firstChild);
    } else {
        this.textElement_.appendChild(this.arrow_);
    }
    this.textElement_.setAttribute('text-anchor', 'start');
    this.textElement_.setAttribute('x', 0);

    this.size_.height = 30;
    this.size_.width = Blockly.Field.getCachedWidth(this.textElement_);
};

Blockly.BlockSvg.SEP_SPACE_X = 20;
Blockly.Field.prototype.init = function() {
    if (this.fieldGroup_) {
        // Field has already been initialized once.
        return;
    }
    // Build the DOM.
    this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null);
    if (!this.visible_) {
        this.fieldGroup_.style.display = 'none';
    }
    this.borderRect_ = Blockly.utils.createSvgElement(
        'rect',
        {
            rx: 4,
            ry: 4,
            x: -Blockly.BlockSvg.SEP_SPACE_X / 2,
            y: 0,
            height: 16,
        },
        this.fieldGroup_
    );
    this.textElement_ = Blockly.utils.createSvgElement(
        'text',
        { class: 'blocklyText', y: this.size_.height - 10 },
        this.fieldGroup_
    );

    this.updateEditable();
    this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
    this.mouseDownWrapper_ = Blockly.bindEventWithChecks_(this.fieldGroup_, 'mousedown', this, this.onMouseDown_);
    // Force a render.
    this.render_();
};
Blockly.FieldLabel.prototype.init = function() {
    if (this.textElement_) {
        // Text has already been initialized once.
        return;
    }
    // Build the DOM.
    this.textElement_ = Blockly.utils.createSvgElement(
        'text',
        { class: 'blocklyText', y: this.size_.height - 2 },
        null
    );
    if (this.class_) {
        Blockly.utils.addClass(this.textElement_, this.class_);
    }
    if (!this.visible_) {
        this.textElement_.style.display = 'none';
    }
    this.sourceBlock_.getSvgRoot().appendChild(this.textElement_);

    // Configure the field to be transparent with respect to tooltips.
    this.textElement_.tooltip = this.sourceBlock_;
    Blockly.Tooltip.bindMouseEvents(this.textElement_);
    // Force a render.
    this.render_();
};
Blockly.BlockSvg.prototype.renderCompute_ = function(iconWidth) {
    const inputList = this.inputList;
    const inputRows = [];
    inputRows.rightEdge = iconWidth + Blockly.BlockSvg.SEP_SPACE_X * 2;
    if (this.previousConnection || this.nextConnection) {
        inputRows.rightEdge = Math.max(
            inputRows.rightEdge,
            Blockly.BlockSvg.NOTCH_WIDTH + Blockly.BlockSvg.SEP_SPACE_X
        );
    }
    let fieldValueWidth = 0; // Width of longest external value field.
    let fieldStatementWidth = 0; // Width of longest statement field.
    let hasValue = false;
    let hasStatement = false;
    let hasDummy = false;
    let lastType;
    const isInline = this.getInputsInline() && !this.isCollapsed();
    for (var i = 0, input; (input = inputList[i]); i++) {
        if (!input.isVisible()) {
            continue;
        }
        var row;
        if (!isInline || !lastType || lastType == Blockly.NEXT_STATEMENT || input.type == Blockly.NEXT_STATEMENT) {
            // Create new row.
            lastType = input.type;
            row = [];
            if (isInline && input.type != Blockly.NEXT_STATEMENT) {
                row.type = Blockly.BlockSvg.INLINE;
            } else {
                row.type = input.type;
            }
            row.height = 0;
            inputRows.push(row);
        } else {
            row = inputRows[inputRows.length - 1];
        }
        row.push(input);

        // Compute minimum input size.
        input.renderHeight = Blockly.BlockSvg.MIN_BLOCK_Y;
        // The width is currently only needed for inline value inputs.
        if (isInline && input.type == Blockly.INPUT_VALUE) {
            input.renderWidth = Blockly.BlockSvg.TAB_WIDTH + Blockly.BlockSvg.SEP_SPACE_X * 1.25;
        } else {
            input.renderWidth = 0;
        }
        // Expand input size if there is a connection.
        if (input.connection && input.connection.isConnected()) {
            const linkedBlock = input.connection.targetBlock();
            const bBox = linkedBlock.getHeightWidth();
            input.renderHeight = Math.max(input.renderHeight, bBox.height);
            input.renderWidth = Math.max(input.renderWidth, bBox.width);
        }
        // Blocks have a one pixel shadow that should sometimes overhang.
        if (!isInline && i == inputList.length - 1) {
            // Last value input should overhang.
            input.renderHeight--;
        } else if (
            !isInline &&
            input.type == Blockly.INPUT_VALUE &&
            inputList[i + 1] &&
            inputList[i + 1].type == Blockly.NEXT_STATEMENT
        ) {
            // Value input above statement input should overhang.
            input.renderHeight--;
        }

        row.height = Math.max(row.height, input.renderHeight);
        input.fieldWidth = 0;
        if (inputRows.length == 1) {
            // The first row gets shifted to accommodate any icons.
            input.fieldWidth += this.RTL ? -iconWidth : iconWidth;
        }
        let previousFieldEditable = false;
        for (var j = 0, field; (field = input.fieldRow[j]); j++) {
            if (j != 0) {
                input.fieldWidth += Blockly.BlockSvg.SEP_SPACE_X;
            }
            // Get the dimensions of the field.
            const fieldSize = field.getSize();
            field.renderWidth = fieldSize.width;
            field.renderSep = previousFieldEditable && field.EDITABLE ? Blockly.BlockSvg.SEP_SPACE_X : 0;
            input.fieldWidth += field.renderWidth + field.renderSep;
            row.height = Math.max(row.height, fieldSize.height) + 1;
            previousFieldEditable = field.EDITABLE;
        }

        if (row.type != Blockly.BlockSvg.INLINE) {
            if (row.type == Blockly.NEXT_STATEMENT) {
                hasStatement = true;
                fieldStatementWidth = Math.max(fieldStatementWidth, input.fieldWidth);
            } else {
                if (row.type == Blockly.INPUT_VALUE) {
                    hasValue = true;
                } else if (row.type == Blockly.DUMMY_INPUT) {
                    hasDummy = true;
                }
                fieldValueWidth = Math.max(fieldValueWidth, input.fieldWidth);
            }
        }
    }

    // Make inline rows a bit thicker in order to enclose the values.
    for (var y = 0, row; (row = inputRows[y]); y++) {
        row.thicker = false;
        if (row.type == Blockly.BlockSvg.INLINE) {
            for (var z = 0, input; (input = row[z]); z++) {
                if (input.type == Blockly.INPUT_VALUE) {
                    row.height += 2 * Blockly.BlockSvg.INLINE_PADDING_Y;
                    row.thicker = true;
                    break;
                }
            }
        }
    }

    // Compute the statement edge.
    // This is the width of a block where statements are nested.
    inputRows.statementEdge = 2 * Blockly.BlockSvg.SEP_SPACE_X + fieldStatementWidth;
    // Compute the preferred right edge.  Inline blocks may extend beyond.
    // This is the width of the block where external inputs connect.
    if (hasStatement) {
        inputRows.rightEdge = Math.max(inputRows.rightEdge, inputRows.statementEdge + Blockly.BlockSvg.NOTCH_WIDTH);
    }
    if (hasValue) {
        inputRows.rightEdge = Math.max(
            inputRows.rightEdge,
            fieldValueWidth + Blockly.BlockSvg.SEP_SPACE_X * 2 + Blockly.BlockSvg.TAB_WIDTH
        );
    } else if (hasDummy) {
        inputRows.rightEdge = Math.max(inputRows.rightEdge, fieldValueWidth + Blockly.BlockSvg.SEP_SPACE_X * 2);
    }

    inputRows.hasValue = hasValue;
    inputRows.hasStatement = hasStatement;
    inputRows.hasDummy = hasDummy;
    return inputRows;
};
Blockly.FieldLabel.prototype.init = function() {
    if (this.textElement_) {
        // Text has already been initialized once.
        return;
    }
    // Build the DOM.
    this.textElement_ = Blockly.utils.createSvgElement(
        'text',
        { class: 'blocklyText', y: this.size_.height - 3 },
        null
    );
    if (this.class_) {
        Blockly.utils.addClass(this.textElement_, this.class_);
    }
    if (!this.visible_) {
        this.textElement_.style.display = 'none';
    }
    this.sourceBlock_.getSvgRoot().appendChild(this.textElement_);

    // Configure the field to be transparent with respect to tooltips.
    this.textElement_.tooltip = this.sourceBlock_;
    Blockly.Tooltip.bindMouseEvents(this.textElement_);
    // Force a render.
    this.render_();
};
// Override inline editor blockly
Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) {
    Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_());
    var div = Blockly.WidgetDiv.DIV;
    // Create the input.
    var htmlInput = document.createElement('input');
    htmlInput.className = 'blocklyHtmlInput';
    htmlInput.setAttribute('spellcheck', this.spellcheck_);
    htmlInput.setAttribute('data-lpignore', 'true');
    var fontSize = Blockly.FieldTextInput.FONTSIZE * this.workspace_.scale + 'pt';
    div.style.fontSize = fontSize;
    htmlInput.style.fontSize = fontSize;

    Blockly.FieldTextInput.htmlInput_ = htmlInput;
    div.appendChild(htmlInput);

    htmlInput.value = htmlInput.defaultValue = this.text_;
    htmlInput.oldValue_ = null;
    this.validate_();
    this.resizeEditor_();
    if (!quietInput) {
        htmlInput.focus();
        htmlInput.select();
    }

    this.bindEvents_(htmlInput);
};
<commit_msg>Move custom functions to customBlockly, add attachShadowBlock function<commit_after>import { translate } from '../../../common/utils/tools';

/* eslint-disable */
Blockly.WorkspaceAudio.prototype.preload = function() {};
Blockly.FieldDropdown.prototype.render_ = function() {
    if (!this.visible_) {
        this.size_.width = 0;
        return;
    }
    if (this.sourceBlock_ && this.arrow_) {
        // Update arrow's colour.
        this.arrow_.style.fill = this.sourceBlock_.getColour();
    }
    goog.dom.removeChildren(this.textElement_);
    goog.dom.removeNode(this.imageElement_);
    this.imageElement_ = null;

    if (this.imageJson_) {
        this.renderSelectedImage_();
    } else {
        this.renderSelectedText_();
    }
    this.borderRect_.setAttribute('height', this.size_.height - 8);
    this.borderRect_.setAttribute('width', this.size_.width + Blockly.BlockSvg.SEP_SPACE_X);
};
Blockly.FieldDropdown.prototype.renderSelectedText_ = function() {
    // Text option is selected.
    // Replace the text.
    const textNode = document.createTextNode(this.getDisplayText_());
    this.textElement_.appendChild(textNode);
    // Insert dropdown arrow.
    if (this.sourceBlock_.RTL) {
        this.textElement_.insertBefore(this.arrow_, this.textElement_.firstChild);
    } else {
        this.textElement_.appendChild(this.arrow_);
    }
    this.textElement_.setAttribute('text-anchor', 'start');
    this.textElement_.setAttribute('x', 0);

    this.size_.height = 30;
    this.size_.width = Blockly.Field.getCachedWidth(this.textElement_);
};

Blockly.BlockSvg.SEP_SPACE_X = 20;
Blockly.Field.prototype.init = function() {
    if (this.fieldGroup_) {
        // Field has already been initialized once.
        return;
    }
    // Build the DOM.
    this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null);
    if (!this.visible_) {
        this.fieldGroup_.style.display = 'none';
    }
    this.borderRect_ = Blockly.utils.createSvgElement(
        'rect',
        {
            rx: 4,
            ry: 4,
            x: -Blockly.BlockSvg.SEP_SPACE_X / 2,
            y: 0,
            height: 16,
        },
        this.fieldGroup_
    );
    this.textElement_ = Blockly.utils.createSvgElement(
        'text',
        { class: 'blocklyText', y: this.size_.height - 10 },
        this.fieldGroup_
    );

    this.updateEditable();
    this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
    this.mouseDownWrapper_ = Blockly.bindEventWithChecks_(this.fieldGroup_, 'mousedown', this, this.onMouseDown_);
    // Force a render.
    this.render_();
};
Blockly.FieldLabel.prototype.init = function() {
    if (this.textElement_) {
        // Text has already been initialized once.
        return;
    }
    // Build the DOM.
    this.textElement_ = Blockly.utils.createSvgElement(
        'text',
        { class: 'blocklyText', y: this.size_.height - 2 },
        null
    );
    if (this.class_) {
        Blockly.utils.addClass(this.textElement_, this.class_);
    }
    if (!this.visible_) {
        this.textElement_.style.display = 'none';
    }
    this.sourceBlock_.getSvgRoot().appendChild(this.textElement_);

    // Configure the field to be transparent with respect to tooltips.
    this.textElement_.tooltip = this.sourceBlock_;
    Blockly.Tooltip.bindMouseEvents(this.textElement_);
    // Force a render.
    this.render_();
};
Blockly.BlockSvg.prototype.renderCompute_ = function(iconWidth) {
    const inputList = this.inputList;
    const inputRows = [];
    inputRows.rightEdge = iconWidth + Blockly.BlockSvg.SEP_SPACE_X * 2;
    if (this.previousConnection || this.nextConnection) {
        inputRows.rightEdge = Math.max(
            inputRows.rightEdge,
            Blockly.BlockSvg.NOTCH_WIDTH + Blockly.BlockSvg.SEP_SPACE_X
        );
    }
    let fieldValueWidth = 0; // Width of longest external value field.
    let fieldStatementWidth = 0; // Width of longest statement field.
    let hasValue = false;
    let hasStatement = false;
    let hasDummy = false;
    let lastType;
    const isInline = this.getInputsInline() && !this.isCollapsed();
    for (var i = 0, input; (input = inputList[i]); i++) {
        if (!input.isVisible()) {
            continue;
        }
        var row;
        if (!isInline || !lastType || lastType == Blockly.NEXT_STATEMENT || input.type == Blockly.NEXT_STATEMENT) {
            // Create new row.
            lastType = input.type;
            row = [];
            if (isInline && input.type != Blockly.NEXT_STATEMENT) {
                row.type = Blockly.BlockSvg.INLINE;
            } else {
                row.type = input.type;
            }
            row.height = 0;
            inputRows.push(row);
        } else {
            row = inputRows[inputRows.length - 1];
        }
        row.push(input);

        // Compute minimum input size.
        input.renderHeight = Blockly.BlockSvg.MIN_BLOCK_Y;
        // The width is currently only needed for inline value inputs.
        if (isInline && input.type == Blockly.INPUT_VALUE) {
            input.renderWidth = Blockly.BlockSvg.TAB_WIDTH + Blockly.BlockSvg.SEP_SPACE_X * 1.25;
        } else {
            input.renderWidth = 0;
        }
        // Expand input size if there is a connection.
        if (input.connection && input.connection.isConnected()) {
            const linkedBlock = input.connection.targetBlock();
            const bBox = linkedBlock.getHeightWidth();
            input.renderHeight = Math.max(input.renderHeight, bBox.height);
            input.renderWidth = Math.max(input.renderWidth, bBox.width);
        }
        // Blocks have a one pixel shadow that should sometimes overhang.
        if (!isInline && i == inputList.length - 1) {
            // Last value input should overhang.
            input.renderHeight--;
        } else if (
            !isInline &&
            input.type == Blockly.INPUT_VALUE &&
            inputList[i + 1] &&
            inputList[i + 1].type == Blockly.NEXT_STATEMENT
        ) {
            // Value input above statement input should overhang.
            input.renderHeight--;
        }

        row.height = Math.max(row.height, input.renderHeight);
        input.fieldWidth = 0;
        if (inputRows.length == 1) {
            // The first row gets shifted to accommodate any icons.
            input.fieldWidth += this.RTL ? -iconWidth : iconWidth;
        }
        let previousFieldEditable = false;
        for (var j = 0, field; (field = input.fieldRow[j]); j++) {
            if (j != 0) {
                input.fieldWidth += Blockly.BlockSvg.SEP_SPACE_X;
            }
            // Get the dimensions of the field.
            const fieldSize = field.getSize();
            field.renderWidth = fieldSize.width;
            field.renderSep = previousFieldEditable && field.EDITABLE ? Blockly.BlockSvg.SEP_SPACE_X : 0;
            input.fieldWidth += field.renderWidth + field.renderSep;
            row.height = Math.max(row.height, fieldSize.height) + 1;
            previousFieldEditable = field.EDITABLE;
        }

        if (row.type != Blockly.BlockSvg.INLINE) {
            if (row.type == Blockly.NEXT_STATEMENT) {
                hasStatement = true;
                fieldStatementWidth = Math.max(fieldStatementWidth, input.fieldWidth);
            } else {
                if (row.type == Blockly.INPUT_VALUE) {
                    hasValue = true;
                } else if (row.type == Blockly.DUMMY_INPUT) {
                    hasDummy = true;
                }
                fieldValueWidth = Math.max(fieldValueWidth, input.fieldWidth);
            }
        }
    }

    // Make inline rows a bit thicker in order to enclose the values.
    for (var y = 0, row; (row = inputRows[y]); y++) {
        row.thicker = false;
        if (row.type == Blockly.BlockSvg.INLINE) {
            for (var z = 0, input; (input = row[z]); z++) {
                if (input.type == Blockly.INPUT_VALUE) {
                    row.height += 2 * Blockly.BlockSvg.INLINE_PADDING_Y;
                    row.thicker = true;
                    break;
                }
            }
        }
    }

    // Compute the statement edge.
    // This is the width of a block where statements are nested.
    inputRows.statementEdge = 2 * Blockly.BlockSvg.SEP_SPACE_X + fieldStatementWidth;
    // Compute the preferred right edge.  Inline blocks may extend beyond.
    // This is the width of the block where external inputs connect.
    if (hasStatement) {
        inputRows.rightEdge = Math.max(inputRows.rightEdge, inputRows.statementEdge + Blockly.BlockSvg.NOTCH_WIDTH);
    }
    if (hasValue) {
        inputRows.rightEdge = Math.max(
            inputRows.rightEdge,
            fieldValueWidth + Blockly.BlockSvg.SEP_SPACE_X * 2 + Blockly.BlockSvg.TAB_WIDTH
        );
    } else if (hasDummy) {
        inputRows.rightEdge = Math.max(inputRows.rightEdge, fieldValueWidth + Blockly.BlockSvg.SEP_SPACE_X * 2);
    }

    inputRows.hasValue = hasValue;
    inputRows.hasStatement = hasStatement;
    inputRows.hasDummy = hasDummy;
    return inputRows;
};
Blockly.FieldLabel.prototype.init = function() {
    if (this.textElement_) {
        // Text has already been initialized once.
        return;
    }
    // Build the DOM.
    this.textElement_ = Blockly.utils.createSvgElement(
        'text',
        { class: 'blocklyText', y: this.size_.height - 3 },
        null
    );
    if (this.class_) {
        Blockly.utils.addClass(this.textElement_, this.class_);
    }
    if (!this.visible_) {
        this.textElement_.style.display = 'none';
    }
    this.sourceBlock_.getSvgRoot().appendChild(this.textElement_);

    // Configure the field to be transparent with respect to tooltips.
    this.textElement_.tooltip = this.sourceBlock_;
    Blockly.Tooltip.bindMouseEvents(this.textElement_);
    // Force a render.
    this.render_();
};
// Override inline editor blockly
Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) {
    Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_());
    var div = Blockly.WidgetDiv.DIV;
    // Create the input.
    var htmlInput = document.createElement('input');
    htmlInput.className = 'blocklyHtmlInput';
    htmlInput.setAttribute('spellcheck', this.spellcheck_);
    htmlInput.setAttribute('data-lpignore', 'true');
    var fontSize = Blockly.FieldTextInput.FONTSIZE * this.workspace_.scale + 'pt';
    div.style.fontSize = fontSize;
    htmlInput.style.fontSize = fontSize;

    Blockly.FieldTextInput.htmlInput_ = htmlInput;
    div.appendChild(htmlInput);

    htmlInput.value = htmlInput.defaultValue = this.text_;
    htmlInput.oldValue_ = null;
    this.validate_();
    this.resizeEditor_();
    if (!quietInput) {
        htmlInput.focus();
        htmlInput.select();
    }

    this.bindEvents_(htmlInput);
};
const originalContextMenuFn = Blockly.ContextMenu.show;
Blockly.ContextMenu.show = (e, menuOptions, rtl) => {
    // Rename 'Clean up blocks'
    menuOptions.some(option => {
        if (option.text === Blockly.Msg.CLEAN_UP) {
            option.text = translate('Rearrange vertically'); // eslint-disable-line no-param-reassign
            return true;
        }
        return false;
    }) &&
        /* Remove delete all blocks, but only when 'Clean up blocks' is available (i.e. workspace)
         * This allows users to still delete root blocks containing blocks
         */
        menuOptions.some((option, i) => {
            if (
                option.text === Blockly.Msg.DELETE_BLOCK ||
                option.text.replace(/[0-9]+/, '%1') === Blockly.Msg.DELETE_X_BLOCKS
            ) {
                menuOptions.splice(i, 1);
                return true;
            }
            return false;
        });
    // Open the Elev.io widget when clicking 'Help'
    // eslint-disable-next-line no-underscore-dangle
    if (window._elev) {
        menuOptions.some(option => {
            if (option.text === Blockly.Msg.HELP) {
                option.callback = () => window._elev.open(); // eslint-disable-line no-param-reassign, no-underscore-dangle
                return true;
            }
            return false;
        });
    }
    originalContextMenuFn(e, menuOptions, rtl);
};
Blockly.Input.prototype.attachShadowBlock = function(value, name, shadowBlockType) {
    const shadowBlock = this.sourceBlock_.workspace.newBlock(shadowBlockType);
    shadowBlock.setShadow(true);
    shadowBlock.setFieldValue(value, name); // Refactor when using shadow block for strings in future
    shadowBlock.outputConnection.connect(this.connection);
    shadowBlock.initSvg();
    shadowBlock.render();
};
<|endoftext|>