<commit_before>import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles'
import { differenceInYears, parse, format } from 'date-fns/fp'
import _ from 'lodash/fp'
import * as R from 'ramda'
import { useState, React } from 'react'
import * as Yup from 'yup'

import ImagePopper from 'src/components/ImagePopper'
import { FeatureButton } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik'
import { H3, Info3 } from 'src/components/typography'
import {
  OVERRIDE_AUTHORIZED,
  OVERRIDE_REJECTED
} from 'src/pages/Customers/components/propertyCard'
import { ReactComponent as CardIcon } from 'src/styling/icons/ID/card/comet.svg'
import { ReactComponent as PhoneIcon } from 'src/styling/icons/ID/phone/comet.svg'
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/comet.svg'
import { ReactComponent as CustomerListViewReversedIcon } from 'src/styling/icons/circle buttons/customer-list-view/white.svg'
import { ReactComponent as CustomerListViewIcon } from 'src/styling/icons/circle buttons/customer-list-view/zodiac.svg'
import { ReactComponent as OverviewReversedIcon } from 'src/styling/icons/circle buttons/overview/white.svg'
import { ReactComponent as OverviewIcon } from 'src/styling/icons/circle buttons/overview/zodiac.svg'
import { URI } from 'src/utils/apollo'

import styles from './CustomerData.styles.js'
import { EditableCard } from './components'
import { getName } from './helper.js'

const useStyles = makeStyles(styles)

const IMAGE_WIDTH = 165
const IMAGE_HEIGHT = 45
const POPUP_IMAGE_WIDTH = 360
const POPUP_IMAGE_HEIGHT = 240

const Photo = ({ show, src }) => {
  const classes = useStyles({ width: IMAGE_WIDTH })

  return (
    <>
      {show ? (
        <ImagePopper
          src={src}
          width={IMAGE_WIDTH}
          height={IMAGE_HEIGHT}
          popupWidth={POPUP_IMAGE_WIDTH}
          popupHeight={POPUP_IMAGE_HEIGHT}
        />
      ) : (

          <CrossedCameraIcon />

      )}
    </>
  )
}

const CustomerData = ({
  customer,
  updateCustomer,
  replacePhoto,
  editCustomer,
  deleteEditedData
}) => {
  const classes = useStyles()
  const [listView, setListView] = useState(false)

  const idData = R.path(['idCardData'])(customer)
  const rawExpirationDate = R.path(['expirationDate'])(idData)
  const country = R.path(['country'])(idData)
  const rawDob = R.path(['dateOfBirth'])(idData)

  const sanctions = R.path(['sanctions'])(customer)
  const sanctionsAt = R.path(['sanctionsAt'])(customer)
  const sanctionsDisplay = !sanctionsAt
    ? 'Not checked yet'
    : sanctions
    ? 'Passed'
    : 'Failed'

  const customEntries = null // get customer custom entries
  const customRequirements = null // get customer custom requirements

  const isEven = elem => elem % 2 === 0

  const getVisibleCards = _.filter(elem => elem.isAvailable)

  const schemas = {
    idScan: Yup.object().shape({
      name: Yup.string(),
      idNumber: Yup.string(),
      birthDate: Yup.string(),
      age: Yup.string(),
      gender: Yup.string(),
      state: Yup.string(),
      expirationDate: Yup.string()
    }),
    usSsn: Yup.object().shape({
      usSsn: Yup.string()
    }),
    idCardPhoto: Yup.object().shape({
      idCardPhoto: Yup.mixed()
    }),
    frontCamera: Yup.object().shape({
      frontCamera: Yup.mixed()
    })
  }

  const idScanElements = [
    {
      name: 'name',
      label: 'Name',
      component: TextInput
    },
    {
      name: 'idNumber',
      label: 'ID number',
      component: TextInput
    },
    {
      name: 'birthDate',
      label: 'Birth Date',
      component: TextInput
    },
    {
      name: 'age',
      label: 'Age',
      component: TextInput
    },
    {
      name: 'gender',
      label: 'Gender',
      component: TextInput
    },
    {
      name: 'state',
      label: country === 'Canada' ? 'Province' : 'State',
      component: TextInput
    },
    {
      name: 'expirationDate',
      label: 'Expiration Date',
      component: TextInput
    }
  ]

  const usSsnElements = [
    {
      name: 'usSsn',
      label: 'US SSN',
      component: TextInput,
      size: 190
    }
  ]

  const idCardPhotoElements = [{ name: 'idCardPhoto' }]
  const frontCameraElements = [{ name: 'frontCamera' }]

  const initialValues = {
    idScan: {
      name: getName(customer) ?? '',
      idNumber: R.path(['documentNumber'])(idData) ?? '',
      birthDate: (rawDob && format('yyyy-MM-dd', rawDob)) ?? '',
      age: (rawDob && differenceInYears(rawDob, new Date())) ?? '',
      gender: R.path(['gender'])(idData) ?? '',
      state: R.path(['state'])(idData) ?? '',
      expirationDate:
        (rawExpirationDate && format('yyyy-MM-dd', rawExpirationDate)) ?? ''
    },
    usSsn: {
      usSsn: customer.usSsn ?? ''
    },
    frontCamera: {
      frontCamera: null
    },
    idCardPhoto: {
      idCardPhoto: null
    }
  }

  const cards = [
    {
      fields: idScanElements,
      title: 'ID Scan',
      titleIcon: <PhoneIcon className={classes.cardIcon} />,
      state: R.path(['idCardDataOverride'])(customer),
      authorize: () =>
        updateCustomer({ idCardDataOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ idCardDataOverride: OVERRIDE_REJECTED }),
      deleteEditedData: () => deleteEditedData({ idCardData: null }),
      save: values => editCustomer({ idCardData: values }),
      validationSchema: schemas.idScan,
      initialValues: initialValues.idScan,
      isAvailable: !_.isNil(idData)
    },
    {
      title: 'SMS Confirmation',
      titleIcon: <CardIcon className={classes.cardIcon} />,
      authorize: () => {},
      reject: () => {},
      save: () => {},
      isAvailable: false
    },
    {
      title: 'Name',
      titleIcon: <EditIcon className={classes.editIcon} />,
      authorize: () => {},
      reject: () => {},
      save: () => {},
      isAvailable: false
    },
    {
      title: 'Sanctions check',
      titleIcon: <EditIcon className={classes.editIcon} />,
      state: R.path(['sanctionsOverride'])(customer),
      authorize: () =>
        updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }),
      children: <Info3>{sanctionsDisplay}</Info3>,
      isAvailable: !_.isNil(sanctions)
    },
    {
      fields: frontCameraElements,
      title: 'Front facing camera',
      titleIcon: <EditIcon className={classes.editIcon} />,
      state: R.path(['frontCameraOverride'])(customer),
      authorize: () =>
        updateCustomer({ frontCameraOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ frontCameraOverride: OVERRIDE_REJECTED }),
      save: values =>
        replacePhoto({
          newPhoto: values.frontCamera,
          photoType: 'frontCamera'
        }),
      deleteEditedData: () => deleteEditedData({ frontCamera: null }),
      children: customer.frontCameraPath ? (
        <Photo
          show={customer.frontCameraPath}
          src={`${URI}/front-camera-photo/${R.path(['frontCameraPath'])(
            customer
          )}`}
        />
      ) : null,
      hasImage: true,
      validationSchema: schemas.frontCamera,
      initialValues: initialValues.frontCamera,
      isAvailable: !_.isNil(customer.frontCameraPath)
    },
    {
      fields: idCardPhotoElements,
      title: 'ID card image',
      titleIcon: <EditIcon className={classes.editIcon} />,
      state: R.path(['idCardPhotoOverride'])(customer),
      authorize: () =>
        updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ idCardPhotoOverride: OVERRIDE_REJECTED }),
      save: values =>
        replacePhoto({
          newPhoto: values.idCardPhoto,
          photoType: 'idCardPhoto'
        }),
      deleteEditedData: () => deleteEditedData({ idCardPhoto: null }),
      children: customer.idCardPhotoPath ? (
        <Photo
          show={customer.idCardPhotoPath}
          src={`${URI}/id-card-photo/${R.path(['idCardPhotoPath'])(customer)}`}
        />
      ) : null,
      hasImage: true,
      validationSchema: schemas.idCardPhoto,
      initialValues: initialValues.idCardPhoto,
      isAvailable: !_.isNil(customer.idCardPhotoPath)
    },
    {
      fields: usSsnElements,
      title: 'US SSN',
      titleIcon: <CardIcon className={classes.cardIcon} />,
      state: R.path(['usSsnOverride'])(customer),
      authorize: () => updateCustomer({ usSsnOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }),
      save: values => editCustomer({ usSsn: values.usSsn }),
      deleteEditedData: () => deleteEditedData({ usSsn: null }),
      validationSchema: schemas.usSsn,
      initialValues: initialValues.usSsn,
      isAvailable: !_.isNil(customer.usSsn)
    }
  ]

  const editableCard = (
    {
      title,
      authorize,
      reject,
      state,
      titleIcon,
      fields,
      save,
      deleteEditedData,
      children,
      validationSchema,
      initialValues,
      hasImage
    },
    idx
  ) => {
    return (
      <EditableCard
        title={title}
        key={idx}
        authorize={authorize}
        reject={reject}
        state={state}
        titleIcon={titleIcon}
        hasImage={hasImage}
        fields={fields}
        children={children}
        validationSchema={validationSchema}
        initialValues={initialValues}
        save={save}
        deleteEditedData={deleteEditedData}></EditableCard>
    )
  }

  const visibleCards = getVisibleCards(cards)

  return (

{'Customer data'}

        <FeatureButton
          active={!listView}
          className={classes.viewIcons}
          Icon={OverviewIcon}
          InverseIcon={OverviewReversedIcon}
          onClick={() => setListView(false)}
        />
        <FeatureButton
          active={listView}
          className={classes.viewIcons}
          Icon={CustomerListViewIcon}
          InverseIcon={CustomerListViewReversedIcon}
          onClick={() => setListView(true)}></FeatureButton>

        {!listView && customer && (
          <Grid container>
            <Grid container direction="column" item xs={6}>
              {visibleCards.map((elem, idx) => {
                return isEven(idx) ? editableCard(elem, idx) : null
              })}
            </Grid>
            <Grid container direction="column" item xs={6}>
              {visibleCards.map((elem, idx) => {
                return !isEven(idx) ? editableCard(elem, idx) : null
              })}
            </Grid>
          </Grid>
        )}
        {customEntries && (

            Custom data entry

        )}
        {customRequirements && (

            Custom requirements

        )}

  )
}

export default CustomerData
<commit_msg>fix: forms keys names and img size<commit_after>import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles'
import { differenceInYears, parse, format } from 'date-fns/fp'
import _ from 'lodash/fp'
import * as R from 'ramda'
import { useState, React } from 'react'
import * as Yup from 'yup'

import ImagePopper from 'src/components/ImagePopper'
import { FeatureButton } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik'
import { H3, Info3 } from 'src/components/typography'
import {
  OVERRIDE_AUTHORIZED,
  OVERRIDE_REJECTED
} from 'src/pages/Customers/components/propertyCard'
import { ReactComponent as CardIcon } from 'src/styling/icons/ID/card/comet.svg'
import { ReactComponent as PhoneIcon } from 'src/styling/icons/ID/phone/comet.svg'
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/comet.svg'
import { ReactComponent as CustomerListViewReversedIcon } from 'src/styling/icons/circle buttons/customer-list-view/white.svg'
import { ReactComponent as CustomerListViewIcon } from 'src/styling/icons/circle buttons/customer-list-view/zodiac.svg'
import { ReactComponent as OverviewReversedIcon } from 'src/styling/icons/circle buttons/overview/white.svg'
import { ReactComponent as OverviewIcon } from 'src/styling/icons/circle buttons/overview/zodiac.svg'
import { URI } from 'src/utils/apollo'

import styles from './CustomerData.styles.js'
import { EditableCard } from './components'

const useStyles = makeStyles(styles)

const IMAGE_WIDTH = 165
const IMAGE_HEIGHT = 45
const POPUP_IMAGE_WIDTH = 360
const POPUP_IMAGE_HEIGHT = 240

const Photo = ({ show, src }) => {
  const classes = useStyles({ width: IMAGE_WIDTH })

  return (
    <>
      {show ? (
        <ImagePopper
          src={src}
          width={IMAGE_WIDTH}
          height={IMAGE_HEIGHT}
          popupWidth={POPUP_IMAGE_WIDTH}
          popupHeight={POPUP_IMAGE_HEIGHT}
        />
      ) : (

          <CrossedCameraIcon />

      )}
    </>
  )
}

const CustomerData = ({
  customer,
  updateCustomer,
  replacePhoto,
  editCustomer,
  deleteEditedData
}) => {
  const classes = useStyles()
  const [listView, setListView] = useState(false)

  const idData = R.path(['idCardData'])(customer)
  const rawExpirationDate = R.path(['expirationDate'])(idData)
  const rawDob = R.path(['dateOfBirth'])(idData)

  const sanctions = R.path(['sanctions'])(customer)
  const sanctionsAt = R.path(['sanctionsAt'])(customer)
  const sanctionsDisplay = !sanctionsAt
    ? 'Not checked yet'
    : sanctions
    ? 'Passed'
    : 'Failed'

  const customEntries = null // get customer custom entries
  const customRequirements = null // get customer custom requirements

  const isEven = elem => elem % 2 === 0

  const getVisibleCards = _.filter(elem => elem.isAvailable)

  const schemas = {
    idScan: Yup.object().shape({
      firstName: Yup.string().required(),
      lastName: Yup.string().required(),
      documentNumber: Yup.string().required(),
      dateOfBirth: Yup.string().required(),
      gender: Yup.string().required(),
      country: Yup.string().required(),
      expirationDate: Yup.string().required()
    }),
    usSsn: Yup.object().shape({
      usSsn: Yup.string().required()
    }),
    idCardPhoto: Yup.object().shape({
      idCardPhoto: Yup.mixed().required()
    }),
    frontCamera: Yup.object().shape({
      frontCamera: Yup.mixed().required()
    })
  }

  const idScanElements = [
    {
      name: 'firstName',
      label: 'First name',
      component: TextInput
    },
    {
      name: 'documentNumber',
      label: 'ID number',
      component: TextInput
    },
    {
      name: 'dateOfBirth',
      label: 'Birthdate',
      component: TextInput
    },
    {
      name: 'gender',
      label: 'Gender',
      component: TextInput
    },
    {
      name: 'lastName',
      label: 'Last name',
      component: TextInput
    },
    {
      name: 'expirationDate',
      label: 'Expiration Date',
      component: TextInput
    },
    {
      name: 'country',
      label: 'Country',
      component: TextInput
    }
  ]

  const usSsnElements = [
    {
      name: 'usSsn',
      label: 'US SSN',
      component: TextInput,
      size: 190
    }
  ]

  const idCardPhotoElements = [{ name: 'idCardPhoto' }]
  const frontCameraElements = [{ name: 'frontCamera' }]

  const initialValues = {
    idScan: {
      firstName: R.path(['firstName'])(idData) ?? '',
      lastName: R.path(['lastName'])(idData) ?? '',
      documentNumber: R.path(['documentNumber'])(idData) ?? '',
      dateOfBirth: (rawDob && format('yyyy-MM-dd', rawDob)) ?? '',
      gender: R.path(['gender'])(idData) ?? '',
      country: R.path(['country'])(idData) ?? '',
      expirationDate:
        (rawExpirationDate && format('yyyy-MM-dd', rawExpirationDate)) ?? ''
    },
    usSsn: {
      usSsn: customer.usSsn ?? ''
    },
    frontCamera: {
      frontCamera: null
    },
    idCardPhoto: {
      idCardPhoto: null
    }
  }

  const cards = [
    {
      fields: idScanElements,
      title: 'ID Scan',
      titleIcon: <CardIcon className={classes.cardIcon} />,
      state: R.path(['idCardDataOverride'])(customer),
      authorize: () =>
        updateCustomer({ idCardDataOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ idCardDataOverride: OVERRIDE_REJECTED }),
      deleteEditedData: () => deleteEditedData({ idCardData: null }),
      save: values => editCustomer({ idCardData: _.merge(idData, values) }),
      validationSchema: schemas.idScan,
      initialValues: initialValues.idScan,
      isAvailable: !_.isNil(idData)
    },
    {
      title: 'SMS Confirmation',
      titleIcon: <PhoneIcon className={classes.cardIcon} />,
      authorize: () => {},
      reject: () => {},
      save: () => {},
      isAvailable: false
    },
    {
      title: 'Name',
      titleIcon: <EditIcon className={classes.editIcon} />,
      authorize: () => {},
      reject: () => {},
      save: () => {},
      isAvailable: false
    },
    {
      title: 'Sanctions check',
      titleIcon: <EditIcon className={classes.editIcon} />,
      state: R.path(['sanctionsOverride'])(customer),
      authorize: () =>
        updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }),
      children: <Info3>{sanctionsDisplay}</Info3>,
      isAvailable: !_.isNil(sanctions)
    },
    {
      fields: frontCameraElements,
      title: 'Front facing camera',
      titleIcon: <EditIcon className={classes.editIcon} />,
      state: R.path(['frontCameraOverride'])(customer),
      authorize: () =>
        updateCustomer({ frontCameraOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ frontCameraOverride: OVERRIDE_REJECTED }),
      save: values =>
        replacePhoto({
          newPhoto: values.frontCamera,
          photoType: 'frontCamera'
        }),
      deleteEditedData: () => deleteEditedData({ frontCamera: null }),
      children: customer.frontCameraPath ? (
        <Photo
          show={customer.frontCameraPath}
          src={`${URI}/front-camera-photo/${R.path(['frontCameraPath'])(
            customer
          )}`}
        />
      ) : null,
      hasImage: true,
      validationSchema: schemas.frontCamera,
      initialValues: initialValues.frontCamera,
      isAvailable: !_.isNil(customer.frontCameraPath)
    },
    {
      fields: idCardPhotoElements,
      title: 'ID card image',
      titleIcon: <EditIcon className={classes.editIcon} />,
      state: R.path(['idCardPhotoOverride'])(customer),
      authorize: () =>
        updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ idCardPhotoOverride: OVERRIDE_REJECTED }),
      save: values =>
        replacePhoto({
          newPhoto: values.idCardPhoto,
          photoType: 'idCardPhoto'
        }),
      deleteEditedData: () => deleteEditedData({ idCardPhoto: null }),
      children: customer.idCardPhotoPath ? (
        <Photo
          show={customer.idCardPhotoPath}
          src={`${URI}/id-card-photo/${R.path(['idCardPhotoPath'])(customer)}`}
        />
      ) : null,
      hasImage: true,
      validationSchema: schemas.idCardPhoto,
      initialValues: initialValues.idCardPhoto,
      isAvailable: !_.isNil(customer.idCardPhotoPath)
    },
    {
      fields: usSsnElements,
      title: 'US SSN',
      titleIcon: <CardIcon className={classes.cardIcon} />,
      state: R.path(['usSsnOverride'])(customer),
      authorize: () => updateCustomer({ usSsnOverride: OVERRIDE_AUTHORIZED }),
      reject: () => updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }),
      save: values => editCustomer({ usSsn: values.usSsn }),
      deleteEditedData: () => deleteEditedData({ usSsn: null }),
      validationSchema: schemas.usSsn,
      initialValues: initialValues.usSsn,
      isAvailable: !_.isNil(customer.usSsn)
    }
  ]

  const editableCard = (
    {
      title,
      authorize,
      reject,
      state,
      titleIcon,
      fields,
      save,
      deleteEditedData,
      children,
      validationSchema,
      initialValues,
      hasImage
    },
    idx
  ) => {
    return (
      <EditableCard
        title={title}
        key={idx}
        authorize={authorize}
        reject={reject}
        state={state}
        titleIcon={titleIcon}
        hasImage={hasImage}
        fields={fields}
        children={children}
        validationSchema={validationSchema}
        initialValues={initialValues}
        save={save}
        deleteEditedData={deleteEditedData}></EditableCard>
    )
  }

  const visibleCards = getVisibleCards(cards)

  return (

{'Customer data'}

        <FeatureButton
          active={!listView}
          className={classes.viewIcons}
          Icon={OverviewIcon}
          InverseIcon={OverviewReversedIcon}
          onClick={() => setListView(false)}
        />
        <FeatureButton
          active={listView}
          className={classes.viewIcons}
          Icon={CustomerListViewIcon}
          InverseIcon={CustomerListViewReversedIcon}
          onClick={() => setListView(true)}></FeatureButton>

        {!listView && customer && (
          <Grid container>
            <Grid container direction="column" item xs={6}>
              {visibleCards.map((elem, idx) => {
                return isEven(idx) ? editableCard(elem, idx) : null
              })}
            </Grid>
            <Grid container direction="column" item xs={6}>
              {visibleCards.map((elem, idx) => {
                return !isEven(idx) ? editableCard(elem, idx) : null
              })}
            </Grid>
          </Grid>
        )}
        {customEntries && (

            Custom data entry

        )}
        {customRequirements && (

            Custom requirements

        )}

  )
}

export default CustomerData
<|endoftext|>


<commit_before>/* global __SERVER__ */
import fetch from 'isomorphic-fetch'

import {updateJWT} from 'src/common/actions/updateJWT'

let APP_BASE_URL = ''
if (__SERVER__) {
  APP_BASE_URL = require('src/config').server.baseURL
}

export default function getGraphQLFetcher(dispatch, auth, baseUrl = APP_BASE_URL, throwErrors = true) {
  return graphQLParams => {
    const options = {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(graphQLParams),
    }
    if (auth.lgJWT) {
      options.headers = Object.assign(options.headers, {
        Authorization: `Bearer ${auth.lgJWT}`,
      })
    }

    return fetch(`${baseUrl}/graphql`, options)
      .then(resp => {
        if (!resp.ok) {
          resp.text().then(txt => console.error('GraphQL ERROR:', resp.statusText, txt))
          if (throwErrors) {
            throw new Error(`GraphQL ERROR: ${resp.statusText}`)
          }
        }
        // for sliding-sessions, update our JWT from the LearnersGuild-JWT header
        const lgJWT = resp.headers.get('LearnersGuild-JWT')
        if (lgJWT) {
          dispatch(updateJWT(lgJWT))
        }
        return resp.json()
      })
      .then(graphQLResponse => throwErrors ?
        graphQLErrorHandler(graphQLResponse) :
        graphQLResponse)
  }
}

export function graphQLErrorHandler(graphQLResponse) {
  if (graphQLResponse.errors && graphQLResponse.errors.length) {
    // throw the first error
    throw new Error(graphQLResponse.errors[0].message)
  }
  return graphQLResponse
}
<commit_msg>fix client-side graphql error parsing<commit_after>/* global __SERVER__ */
import fetch from 'isomorphic-fetch'

import {updateJWT} from 'src/common/actions/updateJWT'

let APP_BASE_URL = ''
if (__SERVER__) {
  APP_BASE_URL = require('src/config').server.baseURL
}

export default function getGraphQLFetcher(dispatch, auth, baseUrl = APP_BASE_URL, throwErrors = true) {
  return graphQLParams => {
    const options = {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(graphQLParams),
    }
    if (auth.lgJWT) {
      options.headers = Object.assign(options.headers, {
        Authorization: `Bearer ${auth.lgJWT}`,
      })
    }

    return fetch(`${baseUrl}/graphql`, options)
      .then(resp => {
        if (!resp.ok) {
          return resp.json().then(errorResponse => {
            throw errorResponse
          })
        }

        // for sliding-sessions, update our JWT from the LearnersGuild-JWT header
        const lgJWT = resp.headers.get('LearnersGuild-JWT')
        if (lgJWT) {
          dispatch(updateJWT(lgJWT))
        }

        return resp.json()
      })
      .then(graphQLResponse => {
        if (graphQLResponse.errors) {
          throw graphQLResponse
        }

        return graphQLResponse
      })
      .catch(err => {
        if (err && err.errors && err.errors.length > 0) {
          if (throwErrors) {
            throw new Error(err.errors[0].message)
          } else {
            console.error('GraphQL ERROR:', err.stack)
          }
        }
      })
  }
}
<|endoftext|>


<commit_before>import React, {findDOMNode} from 'react';
import {renderIntoDocument, Simulate} from 'react/lib/ReactTestUtils';

import Btn from '../Btn/Btn.js';
import classNames from '../Btn/Btn.css';

describe('Btn', () => {
  it('Should render a button', () => {
    const node = findDOMNode(renderIntoDocument(<Btn>btn</Btn>));
    assert.equal(node.nodeName, 'BUTTON');
  });

  it('Should output an anchor when passed an href', () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn href="#">A link, really. Not a button at all.</Btn>
    ));
    assert.equal(node.nodeName, 'A');
  });

  it('Passes through the className to the actual DOM element', () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn className="TESTMCTESTERSON"/>
    ));
    assert.ok(node.className.match(/\bTESTMCTESTERSON\b/));
  });

  it(`Doesn't pass through all props it's given`, () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn data-someRandoAttribute="derp"/>
    ));
    assert.equal(node.getAttribute('data-someRandoAttribute'), null);
  });

  it('Correctly applies the className for the type', () => {
    ['primary', 'secondary', 'danger', 'white'].forEach(type => {
      const node = findDOMNode(renderIntoDocument(
        <Btn type={type}/>
      ));
      assert.ok(node.className.match(new RegExp(classNames[type])));
    });
  });

  it('Correctly applies the className for the variant', () => {
    ['cta', 'hollow', 'clean'].forEach(variant => {
      const node = findDOMNode(renderIntoDocument(
        <Btn variant={variant}/>
      ));
      assert.ok(node.className.match(new RegExp(classNames[variant])));
    });
  });

  it('Applies the correct CSS class when the fullWidth prop is passed', () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn fullWidth/>
    ));
    assert.ok(node.className.match(new RegExp(classNames.fullWidth)));
  });

  it('Disables correctly as a button', () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn disabled/>
    ));
    assert.ok(node.disabled);
  });

  it(`Can't be disabled if it's an anchor with an href`, () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn href="#" disabled/>
    ));
    assert.notOk(node.disabled);
  });

  it(`Calls the onClick function it's passed`, (done) => {
    const doneOp = () => done();
    const node = findDOMNode(renderIntoDocument(
      <Btn onClick={doneOp}/>
    ));
    Simulate.click(node);
  });
});
<commit_msg>Btn test now reflects the CTA change<commit_after>import React, {findDOMNode} from 'react';
import {renderIntoDocument, Simulate} from 'react/lib/ReactTestUtils';

import Btn from '../Btn/Btn.js';
import classNames from '../Btn/Btn.css';

describe('Btn', () => {
  it('Should render a button', () => {
    const node = findDOMNode(renderIntoDocument(<Btn>btn</Btn>));
    assert.equal(node.nodeName, 'BUTTON');
  });

  it('Should output an anchor when passed an href', () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn href="#">A link, really. Not a button at all.</Btn>
    ));
    assert.equal(node.nodeName, 'A');
  });

  it('Passes through the className to the actual DOM element', () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn className="TESTMCTESTERSON"/>
    ));
    assert.ok(node.className.match(/\bTESTMCTESTERSON\b/));
  });

  it(`Doesn't pass through all props it's given`, () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn data-someRandoAttribute="derp"/>
    ));
    assert.equal(node.getAttribute('data-someRandoAttribute'), null);
  });

  it('Correctly applies the className for the type', () => {
    ['primary', 'secondary', 'danger', 'white'].forEach(type => {
      const node = findDOMNode(renderIntoDocument(
        <Btn type={type}/>
      ));
      assert.ok(node.className.match(new RegExp(classNames[type])));
    });
  });

  it('Correctly applies the className for the variant', () => {
    ['hollow', 'clean'].forEach(variant => {
      const node = findDOMNode(renderIntoDocument(
        <Btn variant={variant}/>
      ));
      assert.ok(node.className.match(new RegExp(classNames[variant])));
    });
  });

  it('Applies the correct CSS class when the fullWidth prop is passed', () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn fullWidth/>
    ));
    assert.ok(node.className.match(new RegExp(classNames.fullWidth)));
  });

  it('Disables correctly as a button', () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn disabled/>
    ));
    assert.ok(node.disabled);
  });

  it(`Can't be disabled if it's an anchor with an href`, () => {
    const node = findDOMNode(renderIntoDocument(
      <Btn href="#" disabled/>
    ));
    assert.notOk(node.disabled);
  });

  it(`Calls the onClick function it's passed`, (done) => {
    const doneOp = () => done();
    const node = findDOMNode(renderIntoDocument(
      <Btn onClick={doneOp}/>
    ));
    Simulate.click(node);
  });
});
<|endoftext|>


<commit_before>import initProject from '../../actions/init-project/initProject'
import initPlugin from '../../actions/init-plugin/initPlugin'

const helpText = `
Options
  -y, --yes Use unattended mode, accepting defaults and using only flags for choices
  --project <projectId> Project ID to use for the studio
  --dataset <dataset> Dataset name for the studio
  --dataset-default Set up a project with a public dataset named "production"
  --output-path <path> Path to write studio project to
  --template <template> Project template to use [default: "clean"]
  --visibility <mode> Visibility mode for dataset (public/private)
  --create-project <name> Create a new project with the given name
  --reconfigure Reconfigure Sanity studio in current folder with new project/dataset

Examples
  # Initialize a new project, prompt for required information along the way
  sanity init

  # Initialize a new plugin
  sanity init plugin

  # Initialize a new project with a public dataset named "production"
  sanity init --dataset-default

  # Initialize a project with the given project ID and dataset to the given path
  sanity init -y --project abc123 --dataset production --output-path ~/myproj

  # Initialize a project with the given project ID and dataset using the moviedb
  # template to the given path
  sanity init -y --project abc123 --dataset staging --template moviedb --output-path .

  # Create a brand new project with name "Movies Unlimited"
  sanity init -y \\
    --create-project "Movies Unlimited" \\
    --dataset moviedb \\
    --visibility private \\
    --template moviedb \\
    --output-path /Users/espenh/movies-unlimited
`

export default {
  name: 'init',
  signature: 'init [plugin]',
  description: 'Initialize a new Sanity project or plugin',
  helpText,
  action: (args, context) => {
    const [type] = args.argsWithoutOptions

    if (!type) {
      return initProject(args, context)
    }

    if (type === 'plugin') {
      return initPlugin(args, context)
    }

    return Promise.reject(new Error(`Unknown init type "${type}"`))
  },
}
<commit_msg>fix(cli): correct double init in usage<commit_after>import initProject from '../../actions/init-project/initProject'
import initPlugin from '../../actions/init-plugin/initPlugin'

const helpText = `
Options
  -y, --yes Use unattended mode, accepting defaults and using only flags for choices
  --project <projectId> Project ID to use for the studio
  --dataset <dataset> Dataset name for the studio
  --dataset-default Set up a project with a public dataset named "production"
  --output-path <path> Path to write studio project to
  --template <template> Project template to use [default: "clean"]
  --visibility <mode> Visibility mode for dataset (public/private)
  --create-project <name> Create a new project with the given name
  --reconfigure Reconfigure Sanity studio in current folder with new project/dataset

Examples
  # Initialize a new project, prompt for required information along the way
  sanity init

  # Initialize a new plugin
  sanity init plugin

  # Initialize a new project with a public dataset named "production"
  sanity init --dataset-default

  # Initialize a project with the given project ID and dataset to the given path
  sanity init -y --project abc123 --dataset production --output-path ~/myproj

  # Initialize a project with the given project ID and dataset using the moviedb
  # template to the given path
  sanity init -y --project abc123 --dataset staging --template moviedb --output-path .

  # Create a brand new project with name "Movies Unlimited"
  sanity init -y \\
    --create-project "Movies Unlimited" \\
    --dataset moviedb \\
    --visibility private \\
    --template moviedb \\
    --output-path /Users/espenh/movies-unlimited
`

export default {
  name: 'init',
  signature: '[plugin]',
  description: 'Initialize a new Sanity project or plugin',
  helpText,
  action: (args, context) => {
    const [type] = args.argsWithoutOptions

    if (!type) {
      return initProject(args, context)
    }

    if (type === 'plugin') {
      return initPlugin(args, context)
    }

    return Promise.reject(new Error(`Unknown init type "${type}"`))
  },
}
<|endoftext|>


<commit_before>import { props, define, hasNativeShadowDomSupport } from '@bolt/core/utils';
import { withLitHtml, html } from '@bolt/core/renderers/renderer-lit-html';

import { convertInitialTags } from '@bolt/core/decorators';
import classNames from 'classnames/bind';
import textStyles from '@bolt/components-text/index.scss';
import styles from './blockquote.scss';
import schema from '../blockquote.schema.yml';
import { AuthorImage, AuthorName, AuthorTitle } from './Author';

let cx = classNames.bind([styles, textStyles]);

@define
@convertInitialTags('blockquote') // The first matching tag will have its attributes converted to component props
class BoltBlockquote extends withLitHtml() {
  static is = 'bolt-blockquote';

  static props = {
    size: props.string,
    alignItems: props.string,
    border: props.string,
    indent: props.boolean,
    fullBleed: props.boolean,
    authorName: props.string,
    authorTitle: props.string,
    authorImage: props.string,
  };

  // https://github.com/WebReflection/document-register-element#upgrading-the-constructor-context
  constructor(self) {
    self = super(self);
    self.useShadow = hasNativeShadowDomSupport;
    self.schema = this.getModifiedSchema(schema);
    return self;
  }

  rendered() {
    super.rendered && super.rendered();
    const self = this;

    // @todo: I've added this.useShadow here to exclude IE.
    // In IE-only this mutation callback causes multiple re-renders
    // and causes component to disappear.
    if (window.MutationObserver && this.useShadow) {
      // Re-generate slots + re-render when mutations are observed
      const mutationCallback = function(mutationsList, observer) {
        self.slots = self._checkSlots();
        self.triggerUpdate();

        // todo: refactor to check for slotted content, light OR shadow DOM
        // mutationsList.forEach(mutation => {
        //   if (mutation.type === 'childList') {
        //     if (mutation.target.parentNode === self) {
        //       console.log(mutation.target);
        //     }
        //   }
        // });
      };

      // Create an observer instance linked to the callback function
      self.observer = new MutationObserver(mutationCallback);

      // Start observing the target node for configured mutations
      self.observer.observe(this, {
        attributes: false,
        childList: true,
        subtree: true,
      });
    }
  }

  disconnected() {
    super.disconnected && super.disconnected();

    // remove MutationObserver if supported + exists
    if (window.MutationObserver && this.observer) {
      this.observer.disconnect();
    }
  }

  getModifiedSchema(schema) {
    var modifiedSchema = schema;

    // Remove "content" from schema, does not apply to web component.
    for (let property in modifiedSchema.properties) {
      if (property === 'content') {
        delete modifiedSchema.properties[property];
      }
    }

    const index = modifiedSchema.required.indexOf('content');
    modifiedSchema.required.splice(index, 1);

    return modifiedSchema;
  }

  getAlignItemsOption(prop) {
    switch (prop) {
      case 'right':
        return 'end';
      case 'center':
        return 'center';
      default:
        // left => start
        return 'start';
    }
  }

  getBorderOption(prop) {
    switch (prop) {
      case 'none':
        return 'borderless';
      case 'horizontal':
        return 'bordered-horizontal';
      default:
        // vertical => bordered-vertical
        return 'bordered-vertical';
    }
  }

  // automatically adds classes for the first and last slotted item (in the default slot) to help with tricky ::slotted selectors
  addClassesToSlottedChildren() {
    if (this.slots) {
      if (this.slots.default) {
        const defaultSlot = [];

        this.slots.default.forEach(item => {
          if (item.tagName) {
            item.classList.remove('is-first-child');
            item.classList.remove('is-last-child'); // clean up existing classes
            defaultSlot.push(item);
          }
        });

        if (defaultSlot[0]) {
          defaultSlot[0].classList.add('is-first-child');

          if (defaultSlot.length === 1) {
            defaultSlot[0].classList.add('is-last-child');
          }
        }

        if (defaultSlot[defaultSlot.length - 1]) {
          defaultSlot[defaultSlot.length - 1].classList.add('is-last-child');
        }
      }
    }
  }

  render() {
    // validate the original prop data passed along -- returns back the validated data w/ added default values
    const {
      size,
      alignItems,
      border,
      indent,
      fullBleed,
      authorName,
      authorTitle,
      authorImage,
    } = this.validateProps(this.props);

    const classes = cx('c-bolt-blockquote', {
      [`c-bolt-blockquote--${size}`]: size,
      [`c-bolt-blockquote--align-items-${this.getAlignItemsOption(
        alignItems,
      )}`]: this.getAlignItemsOption(alignItems),
      [`c-bolt-blockquote--${this.getBorderOption(
        border,
      )}`]: this.getBorderOption(border),
      [`c-bolt-blockquote--indented`]: indent,
      [`c-bolt-blockquote--full`]: fullBleed,
    });

    let footerItems = [];
    footerItems.push(AuthorImage(this), AuthorName(this), AuthorTitle(this));

    this.addClassesToSlottedChildren();

    const textClasses = cx(
      'c-bolt-text-v2',
      'c-bolt-text-v2--block',
      'c-bolt-text-v2--body',
      'c-bolt-text-v2--font-size-xlarge',
      'c-bolt-text-v2--font-weight-semibold',
      'c-bolt-text-v2--font-style-regular',
      'c-bolt-text-v2--color-theme-headline',
      'c-bolt-text-v2--letter-spacing-regular',
      'c-bolt-text-v2--align-inherit',
      'c-bolt-text-v2--text-transform-regular',
      'c-bolt-text-v2--line-height-regular',
      'c-bolt-text-v2--opacity-100',
    );

    return html`
      ${this.addStyles([styles, textStyles])}

        ${this.slots.logo
          ? html`

                ${this.slot('logo')}

            `
          : ''}

            ${this.slot('default')}

        ${footerItems.length > 0
          ? html`

                ${footerItems.map(
                  footerItem => html`

                      ${footerItem}

                  `,
                )}

            `
          : ''}

    `;
  }
}

export { BoltBlockquote };

// @todo: Original return statement - backing this out until `bolt-text` is ready for release
// return html`
// ${this.addStyles([styles])}
//

//   ${this.slots.logo
//     ? html`
//

//           ${this.slot('logo')}
//

//       `
//     : ''}
//

//     <bolt-text
//       tag="div"
//       font-size="${size}"
//       font-weight="semibold"
//       color="theme-headline"
//     >
//       ${this.slot('default')}
//     </bolt-text>
//

//   ${footerItems.length > 0
//     ? html`
//
//           ${footerItems.map(
//             footerItem => html`
//

//                 ${footerItem}
//

//             `,
//           )}
//
//       `
//     : ''}
//

// `;
// }
<commit_msg>chore: add PR link to code comment<commit_after>import { props, define, hasNativeShadowDomSupport } from '@bolt/core/utils';
import { withLitHtml, html } from '@bolt/core/renderers/renderer-lit-html';

import { convertInitialTags } from '@bolt/core/decorators';
import classNames from 'classnames/bind';
import textStyles from '@bolt/components-text/index.scss';
import styles from './blockquote.scss';
import schema from '../blockquote.schema.yml';
import { AuthorImage, AuthorName, AuthorTitle } from './Author';

let cx = classNames.bind([styles, textStyles]);

@define
@convertInitialTags('blockquote') // The first matching tag will have its attributes converted to component props
class BoltBlockquote extends withLitHtml() {
  static is = 'bolt-blockquote';

  static props = {
    size: props.string,
    alignItems: props.string,
    border: props.string,
    indent: props.boolean,
    fullBleed: props.boolean,
    authorName: props.string,
    authorTitle: props.string,
    authorImage: props.string,
  };

  // https://github.com/WebReflection/document-register-element#upgrading-the-constructor-context
  constructor(self) {
    self = super(self);
    self.useShadow = hasNativeShadowDomSupport;
    self.schema = this.getModifiedSchema(schema);
    return self;
  }

  rendered() {
    super.rendered && super.rendered();
    const self = this;

    // @todo: I've added this.useShadow here to exclude IE.
    // In IE-only this mutation callback causes multiple re-renders
    // and causes component to disappear.
    if (window.MutationObserver && this.useShadow) {
      // Re-generate slots + re-render when mutations are observed
      const mutationCallback = function(mutationsList, observer) {
        self.slots = self._checkSlots();
        self.triggerUpdate();

        // todo: refactor to check for slotted content, light OR shadow DOM
        // mutationsList.forEach(mutation => {
        //   if (mutation.type === 'childList') {
        //     if (mutation.target.parentNode === self) {
        //       console.log(mutation.target);
        //     }
        //   }
        // });
      };

      // Create an observer instance linked to the callback function
      self.observer = new MutationObserver(mutationCallback);

      // Start observing the target node for configured mutations
      self.observer.observe(this, {
        attributes: false,
        childList: true,
        subtree: true,
      });
    }
  }

  disconnected() {
    super.disconnected && super.disconnected();

    // remove MutationObserver if supported + exists
    if (window.MutationObserver && this.observer) {
      this.observer.disconnect();
    }
  }

  getModifiedSchema(schema) {
    var modifiedSchema = schema;

    // Remove "content" from schema, does not apply to web component.
    for (let property in modifiedSchema.properties) {
      if (property === 'content') {
        delete modifiedSchema.properties[property];
      }
    }

    const index = modifiedSchema.required.indexOf('content');
    modifiedSchema.required.splice(index, 1);

    return modifiedSchema;
  }

  getAlignItemsOption(prop) {
    switch (prop) {
      case 'right':
        return 'end';
      case 'center':
        return 'center';
      default:
        // left => start
        return 'start';
    }
  }

  getBorderOption(prop) {
    switch (prop) {
      case 'none':
        return 'borderless';
      case 'horizontal':
        return 'bordered-horizontal';
      default:
        // vertical => bordered-vertical
        return 'bordered-vertical';
    }
  }

  // automatically adds classes for the first and last slotted item (in the default slot) to help with tricky ::slotted selectors
  addClassesToSlottedChildren() {
    if (this.slots) {
      if (this.slots.default) {
        const defaultSlot = [];

        this.slots.default.forEach(item => {
          if (item.tagName) {
            item.classList.remove('is-first-child');
            item.classList.remove('is-last-child'); // clean up existing classes
            defaultSlot.push(item);
          }
        });

        if (defaultSlot[0]) {
          defaultSlot[0].classList.add('is-first-child');

          if (defaultSlot.length === 1) {
            defaultSlot[0].classList.add('is-last-child');
          }
        }

        if (defaultSlot[defaultSlot.length - 1]) {
          defaultSlot[defaultSlot.length - 1].classList.add('is-last-child');
        }
      }
    }
  }

  render() {
    // validate the original prop data passed along -- returns back the validated data w/ added default values
    const {
      size,
      alignItems,
      border,
      indent,
      fullBleed,
      authorName,
      authorTitle,
      authorImage,
    } = this.validateProps(this.props);

    const classes = cx('c-bolt-blockquote', {
      [`c-bolt-blockquote--${size}`]: size,
      [`c-bolt-blockquote--align-items-${this.getAlignItemsOption(
        alignItems,
      )}`]: this.getAlignItemsOption(alignItems),
      [`c-bolt-blockquote--${this.getBorderOption(
        border,
      )}`]: this.getBorderOption(border),
      [`c-bolt-blockquote--indented`]: indent,
      [`c-bolt-blockquote--full`]: fullBleed,
    });

    let footerItems = [];
    footerItems.push(AuthorImage(this), AuthorName(this), AuthorTitle(this));

    this.addClassesToSlottedChildren();

    const textClasses = cx(
      'c-bolt-text-v2',
      'c-bolt-text-v2--block',
      'c-bolt-text-v2--body',
      'c-bolt-text-v2--font-size-xlarge',
      'c-bolt-text-v2--font-weight-semibold',
      'c-bolt-text-v2--font-style-regular',
      'c-bolt-text-v2--color-theme-headline',
      'c-bolt-text-v2--letter-spacing-regular',
      'c-bolt-text-v2--align-inherit',
      'c-bolt-text-v2--text-transform-regular',
      'c-bolt-text-v2--line-height-regular',
      'c-bolt-text-v2--opacity-100',
    );

    return html`
      ${this.addStyles([styles, textStyles])}

        ${this.slots.logo
          ? html`

                ${this.slot('logo')}

            `
          : ''}

            ${this.slot('default')}

        ${footerItems.length > 0
          ? html`

                ${footerItems.map(
                  footerItem => html`

                      ${footerItem}

                  `,
                )}

            `
          : ''}

    `;
  }
}

export { BoltBlockquote };

// @todo: Original return statement - backing this out until `bolt-text` is ready for release
// See: https://github.com/bolt-design-system/bolt/pull/1167
// return html`
// ${this.addStyles([styles])}
//

//   ${this.slots.logo
//     ? html`
//

//           ${this.slot('logo')}
//

//       `
//     : ''}
//

//     <bolt-text
//       tag="div"
//       font-size="${size}"
//       font-weight="semibold"
//       color="theme-headline"
//     >
//       ${this.slot('default')}
//     </bolt-text>
//

//   ${footerItems.length > 0
//     ? html`
//
//           ${footerItems.map(
//             footerItem => html`
//

//                 ${footerItem}
//

//             `,
//           )}
//
//       `
//     : ''}
//

// `;
// }
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */

import CustomElementInternals from './CustomElementInternals.js';
import DocumentConstructionObserver from './DocumentConstructionObserver.js';
import Deferred from './Deferred.js';
import * as Utilities from './Utilities.js';

/**
 * @extends {window.CustomElementRegistry}
 */
export default class CustomElementRegistry {

  /**
   * @param {!CustomElementInternals} internals
   */
  constructor(internals) {
    /**
     * @private
     * @type {!Map<string, !CustomElementDefinition>}
     */
    this._localNameToDefinition = new Map();

    /**
     * @private
     * @type {!Map<!Function, !CustomElementDefinition>}
     */
    this._constructorToDefinition = new Map();

    /**
     * @private
     * @type {boolean}
     */
    this._elementDefinitionIsRunning = false;

    /**
     * @private
     * @type {!CustomElementInternals}
     */
    this._internals = internals;

    /**
     * @private
     * @type {!Map<string, !Deferred<undefined>>}
     */
    this._whenDefinedDeferred = new Map();

    /**
     * The default flush callback triggers the document walk synchronously.
     * @private
     * @type {!Function}
     */
    this._flushCallback = fn => fn();

    /**
     * @private
     * @type {boolean}
     */
    this._flushPending = false;

    /**
     * @private
     * @type {!Set<string>}
     */
    this._unflushedLocalNames = new Set();

    /**
    * @private
    * @const {!DocumentConstructionObserver|undefined}
    */
    this._documentConstructionObserver = internals.useDocumentConstructionObserver ?
      new DocumentConstructionObserver(internals, document) : undefined;
  }

  /**
   * @param {string} localName
   * @param {function(new: HTMLElement)} constructor
   */
  define(localName, constructor) {
    if (!(constructor instanceof Function)) {
      throw new TypeError('Custom element constructors must be functions.');
    }

    if (!Utilities.isValidCustomElementName(localName)) {
      throw new SyntaxError(`The element name '${localName}' is not valid.`);
    }

    if (this._localNameToDefinition.has(localName)) {
      throw new Error(`A custom element with name '${localName}' has already been defined.`);
    }

    if (this._elementDefinitionIsRunning) {
      throw new Error('A custom element is already being defined.');
    }
    this._elementDefinitionIsRunning = true;

    let connectedCallback;
    let disconnectedCallback;
    let adoptedCallback;
    let attributeChangedCallback;
    let observedAttributes;
    try {
      /** @type {!Object} */
      const prototype = constructor.prototype;
      if (!(prototype instanceof Object)) {
        throw new TypeError('The custom element constructor\'s prototype is not an object.');
      }

      function getCallback(name) {
        const callbackValue = prototype[name];
        if (callbackValue !== undefined && !(callbackValue instanceof Function)) {
          throw new Error(`The '${name}' callback must be a function.`);
        }
        return callbackValue;
      }

      connectedCallback = getCallback('connectedCallback');
      disconnectedCallback = getCallback('disconnectedCallback');
      adoptedCallback = getCallback('adoptedCallback');
      attributeChangedCallback = getCallback('attributeChangedCallback');
      // `observedAttributes` should not be read unless an
      // `attributesChangedCallback` exists
      observedAttributes = (attributeChangedCallback &&
        constructor['observedAttributes']) || [];
    } catch (e) {
      throw e;
    } finally {
      this._elementDefinitionIsRunning = false;
    }

    const definition = {
      localName,
      constructorFunction: constructor,
      connectedCallback,
      disconnectedCallback,
      adoptedCallback,
      attributeChangedCallback,
      observedAttributes,
      constructionStack: [],
    };

    this._localNameToDefinition.set(localName, definition);
    this._constructorToDefinition.set(definition.constructorFunction, definition);
    this._unflushedLocalNames.add(localName);

    // If we've already called the flush callback and it hasn't called back yet,
    // don't call it again.
    if (!this._flushPending) {
      this._flushPending = true;
      this._flushCallback(() => this._flush());
    }
  }

  /**
   * @param {!Node} node
   */
  upgrade(node) {
    this._internals.patchAndUpgradeTree(node);
  }

  /**
   * @private
   */
  _flush() {
    // If no new definitions were defined, don't attempt to flush. This could
    // happen if a flush callback keeps the function it is given and calls it
    // multiple times.
    if (this._flushPending === false) return;
    this._flushPending = false;

    const unflushedLocalNames = this._unflushedLocalNames;

    /**
     * Unupgraded elements with definitions that were defined *before* the last
     * flush, in document order.
     * @type {!Array<!HTMLElement>}
     */
    const elementsWithStableDefinitions = [];

    /**
     * A map from `localName`s of definitions that were defined *after* the last
     * flush to unupgraded elements matching that definition, in document order.
     * @type {!Map<string, !Array<!HTMLElement>>}
     */
    const elementsWithPendingDefinitions = new Map();
    for (const localName of unflushedLocalNames) {
      elementsWithPendingDefinitions.set(localName, []);
    }

    this._internals.patchAndUpgradeTree(document, {
      upgrade: element => {
        // Ignore the element if it has already upgraded or failed to upgrade.
        if (element.__CE_state !== undefined) return;

        const localName = element.localName;

        // If there is an applicable pending definition for the element, add the
        // element to the list of elements to be upgraded with that definition.
        const pendingElements = elementsWithPendingDefinitions.get(localName);
        if (pendingElements) {
          pendingElements.push(element);
        // If there is *any other* applicable definition for the element, add it
        // to the list of elements with stable definitions that need to be upgraded.
        } else if (this._localNameToDefinition.has(localName)) {
          elementsWithStableDefinitions.push(element);
        }
      },
    });

    // Upgrade elements with 'stable' definitions first.
    for (let i = 0; i < elementsWithStableDefinitions.length; i++) {
      this._internals.upgradeReaction(elementsWithStableDefinitions[i]);
    }

    // Upgrade elements with 'pending' definitions in the order they were defined.
    for (const localName of unflushedLocalNames) {
      // Attempt to upgrade all applicable elements.
      const pendingUpgradableElements = elementsWithPendingDefinitions.get(localName);
      for (let i = 0; i < pendingUpgradableElements.length; i++) {
        this._internals.upgradeReaction(pendingUpgradableElements[i]);
      }

      // Resolve any promises created by `whenDefined` for the definition.
      const deferred = this._whenDefinedDeferred.get(localName);
      if (deferred) {
        deferred.resolve(undefined);
      }
    }

    unflushedLocalNames.clear();
  }

  /**
   * @param {string} localName
   * @return {function(new: HTMLElement)|undefined}
   */
  get(localName) {
    const definition = this._localNameToDefinition.get(localName);
    if (definition) {
      return definition.constructorFunction;
    }

    return undefined;
  }

  /**
   * @param {string} localName
   * @return {!Promise<undefined>}
   */
  whenDefined(localName) {
    if (!Utilities.isValidCustomElementName(localName)) {
      return Promise.reject(new SyntaxError(`'${localName}' is not a valid custom element name.`));
    }

    const prior = this._whenDefinedDeferred.get(localName);
    if (prior) {
      return prior.toPromise();
    }

    const deferred = new Deferred();
    this._whenDefinedDeferred.set(localName, deferred);

    const definition = this._localNameToDefinition.get(localName);
    // Resolve immediately only if the given local name has a definition *and*
    // the full document walk to upgrade elements with that local name has
    // already happened.
    if (definition && this._unflushedLocalNames.has(localName)) {
      deferred.resolve(undefined);
    }

    return deferred.toPromise();
  }

  /**
   * @param {function(function())} outer
   */
  polyfillWrapFlushCallback(outer) {
    if (this._documentConstructionObserver) {
      this._documentConstructionObserver.disconnect();
    }
    const inner = this._flushCallback;
    this._flushCallback = flush => outer(() => inner(flush));
  }

  /**
   * @param {string} localName
   * @return {!CustomElementDefinition|undefined}
   */
  internal_localNameToDefinition(localName) {
    return this._localNameToDefinition.get(localName);
  }

  /**
   * @param {!Function} constructor
   * @return {!CustomElementDefinition|undefined}
   */
  internal_constructorToDefinition(constructor) {
    return this._constructorToDefinition.get(constructor);
  }
}

// Closure compiler exports.
window['CustomElementRegistry'] = CustomElementRegistry;
CustomElementRegistry.prototype['define'] = CustomElementRegistry.prototype.define;
CustomElementRegistry.prototype['upgrade'] = CustomElementRegistry.prototype.upgrade;
CustomElementRegistry.prototype['get'] = CustomElementRegistry.prototype.get;
CustomElementRegistry.prototype['whenDefined'] = CustomElementRegistry.prototype.whenDefined;
CustomElementRegistry.prototype['polyfillWrapFlushCallback'] = CustomElementRegistry.prototype.polyfillWrapFlushCallback;
<commit_msg>Remove for-of loops because Closure doesn't compile them in an IE11-compatible way.<commit_after>/**
 * @license
 * Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */

import CustomElementInternals from './CustomElementInternals.js';
import DocumentConstructionObserver from './DocumentConstructionObserver.js';
import Deferred from './Deferred.js';
import * as Utilities from './Utilities.js';

/**
 * @extends {window.CustomElementRegistry}
 */
export default class CustomElementRegistry {

  /**
   * @param {!CustomElementInternals} internals
   */
  constructor(internals) {
    /**
     * @private
     * @type {!Map<string, !CustomElementDefinition>}
     */
    this._localNameToDefinition = new Map();

    /**
     * @private
     * @type {!Map<!Function, !CustomElementDefinition>}
     */
    this._constructorToDefinition = new Map();

    /**
     * @private
     * @type {boolean}
     */
    this._elementDefinitionIsRunning = false;

    /**
     * @private
     * @type {!CustomElementInternals}
     */
    this._internals = internals;

    /**
     * @private
     * @type {!Map<string, !Deferred<undefined>>}
     */
    this._whenDefinedDeferred = new Map();

    /**
     * The default flush callback triggers the document walk synchronously.
     * @private
     * @type {!Function}
     */
    this._flushCallback = fn => fn();

    /**
     * @private
     * @type {boolean}
     */
    this._flushPending = false;

    /**
     * @private
     * @type {!Set<string>}
     */
    this._unflushedLocalNames = new Set();

    /**
    * @private
    * @const {!DocumentConstructionObserver|undefined}
    */
    this._documentConstructionObserver = internals.useDocumentConstructionObserver ?
      new DocumentConstructionObserver(internals, document) : undefined;
  }

  /**
   * @param {string} localName
   * @param {function(new: HTMLElement)} constructor
   */
  define(localName, constructor) {
    if (!(constructor instanceof Function)) {
      throw new TypeError('Custom element constructors must be functions.');
    }

    if (!Utilities.isValidCustomElementName(localName)) {
      throw new SyntaxError(`The element name '${localName}' is not valid.`);
    }

    if (this._localNameToDefinition.has(localName)) {
      throw new Error(`A custom element with name '${localName}' has already been defined.`);
    }

    if (this._elementDefinitionIsRunning) {
      throw new Error('A custom element is already being defined.');
    }
    this._elementDefinitionIsRunning = true;

    let connectedCallback;
    let disconnectedCallback;
    let adoptedCallback;
    let attributeChangedCallback;
    let observedAttributes;
    try {
      /** @type {!Object} */
      const prototype = constructor.prototype;
      if (!(prototype instanceof Object)) {
        throw new TypeError('The custom element constructor\'s prototype is not an object.');
      }

      function getCallback(name) {
        const callbackValue = prototype[name];
        if (callbackValue !== undefined && !(callbackValue instanceof Function)) {
          throw new Error(`The '${name}' callback must be a function.`);
        }
        return callbackValue;
      }

      connectedCallback = getCallback('connectedCallback');
      disconnectedCallback = getCallback('disconnectedCallback');
      adoptedCallback = getCallback('adoptedCallback');
      attributeChangedCallback = getCallback('attributeChangedCallback');
      // `observedAttributes` should not be read unless an
      // `attributesChangedCallback` exists
      observedAttributes = (attributeChangedCallback &&
        constructor['observedAttributes']) || [];
    } catch (e) {
      throw e;
    } finally {
      this._elementDefinitionIsRunning = false;
    }

    const definition = {
      localName,
      constructorFunction: constructor,
      connectedCallback,
      disconnectedCallback,
      adoptedCallback,
      attributeChangedCallback,
      observedAttributes,
      constructionStack: [],
    };

    this._localNameToDefinition.set(localName, definition);
    this._constructorToDefinition.set(definition.constructorFunction, definition);
    this._unflushedLocalNames.add(localName);

    // If we've already called the flush callback and it hasn't called back yet,
    // don't call it again.
    if (!this._flushPending) {
      this._flushPending = true;
      this._flushCallback(() => this._flush());
    }
  }

  /**
   * @param {!Node} node
   */
  upgrade(node) {
    this._internals.patchAndUpgradeTree(node);
  }

  /**
   * @private
   */
  _flush() {
    // If no new definitions were defined, don't attempt to flush. This could
    // happen if a flush callback keeps the function it is given and calls it
    // multiple times.
    if (this._flushPending === false) return;
    this._flushPending = false;

    const unflushedLocalNames = this._unflushedLocalNames;

    /**
     * Unupgraded elements with definitions that were defined *before* the last
     * flush, in document order.
     * @type {!Array<!HTMLElement>}
     */
    const elementsWithStableDefinitions = [];

    /**
     * A map from `localName`s of definitions that were defined *after* the last
     * flush to unupgraded elements matching that definition, in document order.
     * @type {!Map<string, !Array<!HTMLElement>>}
     */
    const elementsWithPendingDefinitions = new Map();
    unflushedLocalNames.forEach(localName => {
      elementsWithPendingDefinitions.set(localName, []);
    });

    this._internals.patchAndUpgradeTree(document, {
      upgrade: element => {
        // Ignore the element if it has already upgraded or failed to upgrade.
        if (element.__CE_state !== undefined) return;

        const localName = element.localName;

        // If there is an applicable pending definition for the element, add the
        // element to the list of elements to be upgraded with that definition.
        const pendingElements = elementsWithPendingDefinitions.get(localName);
        if (pendingElements) {
          pendingElements.push(element);
        // If there is *any other* applicable definition for the element, add it
        // to the list of elements with stable definitions that need to be upgraded.
        } else if (this._localNameToDefinition.has(localName)) {
          elementsWithStableDefinitions.push(element);
        }
      },
    });

    // Upgrade elements with 'stable' definitions first.
    for (let i = 0; i < elementsWithStableDefinitions.length; i++) {
      this._internals.upgradeReaction(elementsWithStableDefinitions[i]);
    }

    // Upgrade elements with 'pending' definitions in the order they were defined.
    unflushedLocalNames.forEach(localName => {
      // Attempt to upgrade all applicable elements.
      const pendingUpgradableElements = elementsWithPendingDefinitions.get(localName);
      for (let i = 0; i < pendingUpgradableElements.length; i++) {
        this._internals.upgradeReaction(pendingUpgradableElements[i]);
      }

      // Resolve any promises created by `whenDefined` for the definition.
      const deferred = this._whenDefinedDeferred.get(localName);
      if (deferred) {
        deferred.resolve(undefined);
      }
    });

    unflushedLocalNames.clear();
  }

  /**
   * @param {string} localName
   * @return {function(new: HTMLElement)|undefined}
   */
  get(localName) {
    const definition = this._localNameToDefinition.get(localName);
    if (definition) {
      return definition.constructorFunction;
    }

    return undefined;
  }

  /**
   * @param {string} localName
   * @return {!Promise<undefined>}
   */
  whenDefined(localName) {
    if (!Utilities.isValidCustomElementName(localName)) {
      return Promise.reject(new SyntaxError(`'${localName}' is not a valid custom element name.`));
    }

    const prior = this._whenDefinedDeferred.get(localName);
    if (prior) {
      return prior.toPromise();
    }

    const deferred = new Deferred();
    this._whenDefinedDeferred.set(localName, deferred);

    const definition = this._localNameToDefinition.get(localName);
    // Resolve immediately only if the given local name has a definition *and*
    // the full document walk to upgrade elements with that local name has
    // already happened.
    if (definition && this._unflushedLocalNames.has(localName)) {
      deferred.resolve(undefined);
    }

    return deferred.toPromise();
  }

  /**
   * @param {function(function())} outer
   */
  polyfillWrapFlushCallback(outer) {
    if (this._documentConstructionObserver) {
      this._documentConstructionObserver.disconnect();
    }
    const inner = this._flushCallback;
    this._flushCallback = flush => outer(() => inner(flush));
  }

  /**
   * @param {string} localName
   * @return {!CustomElementDefinition|undefined}
   */
  internal_localNameToDefinition(localName) {
    return this._localNameToDefinition.get(localName);
  }

  /**
   * @param {!Function} constructor
   * @return {!CustomElementDefinition|undefined}
   */
  internal_constructorToDefinition(constructor) {
    return this._constructorToDefinition.get(constructor);
  }
}

// Closure compiler exports.
window['CustomElementRegistry'] = CustomElementRegistry;
CustomElementRegistry.prototype['define'] = CustomElementRegistry.prototype.define;
CustomElementRegistry.prototype['upgrade'] = CustomElementRegistry.prototype.upgrade;
CustomElementRegistry.prototype['get'] = CustomElementRegistry.prototype.get;
CustomElementRegistry.prototype['whenDefined'] = CustomElementRegistry.prototype.whenDefined;
CustomElementRegistry.prototype['polyfillWrapFlushCallback'] = CustomElementRegistry.prototype.polyfillWrapFlushCallback;
<|endoftext|>


<commit_before>/*global define*/
/*jslint white:true,browser:true*/

define([
    'bluebird',
    'jquery',
    'uuid',
    'base/js/namespace',
    'common/utils',
    'common/appUtils',
    'common/props',
    'common/cellUtils',
    'common/pythonInterop',
    'common/ui',
    'common/jupyter',
    'kb_common/html',
    './widgets/dataCell',
    'custom/custom'
], function(
    Promise,
    $,
    Uuid,
    Jupyter,
    utils,
    AppUtils,
    Props,
    cellUtils,
    PythonInterop,
    UI,
    jupyter,
    html,
    DataCell
) {
    'use strict';

    var t = html.tag,
        div = t('div');

    function specializeCell(cell) {
        cell.minimize = function() {
            var inputArea = this.input.find('.input_area').get(0),
                outputArea = this.element.find('.output_wrapper'),
                showCode = utils.getCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea');

            if (showCode) {
                inputArea.classList.remove('-show');
            }
            outputArea.addClass('hidden');
        };

        cell.maximize = function() {
            var inputArea = this.input.find('.input_area').get(0),
                outputArea = this.element.find('.output_wrapper'),
                showCode = utils.getCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea');

            if (showCode) {
                if (!inputArea.classList.contains('-show')) {
                    inputArea.classList.add('-show');
                    cell.code_mirror.refresh();
                }
            }
            outputArea.removeClass('hidden');
        };

        /*
         * The data cell icon is derived by looking up the type in the
         * narrative configuration.
         */
        cell.renderIcon = function() {
            var inputPrompt = this.element[0].querySelector('[data-element="icon"]'),
                type = Props.getDataItem(cell.metadata, 'kbase.dataCell.objectInfo.type');

            if (inputPrompt) {
                inputPrompt.innerHTML = div({
                    style: { textAlign: 'center' }
                }, [
                    AppUtils.makeTypeIcon(type)
                ]);
            }
        };

        cell.getIcon = function() {
            var type = Props.getDataItem(cell.metadata, 'kbase.dataCell.objectInfo.type'),
                icon = AppUtils.makeToolbarTypeIcon(type);
            return icon;
        };

        cell.toggleCodeInputArea = function() {
            var codeInputArea = this.input.find('.input_area')[0];
            if (codeInputArea) {
                codeInputArea.classList.toggle('-show');
                utils.setCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea', this.isCodeShowing(), true);
                // NB purely for side effect - toolbar refresh
                cell.metadata = cell.metadata;
            }
        };
    }

    function setupCell(cell) {
        if (cell.cell_type !== 'code') {
            return;
        }
        if (!cell.metadata.kbase) {
            return;
        }
        if (cell.metadata.kbase.type !== 'data') {
            return;
        }

        specializeCell(cell);

        // The kbase property is only used for managing runtime state of the cell
        // for kbase. Anything to be persistent should be on the metadata.
        cell.kbase = {};

        // Update metadata.
        utils.setMeta(cell, 'attributes', 'lastLoaded', (new Date()).toUTCString());

        // Ensure code showing is closed to start with.
        // Disable this line to allow this setting to be sticky.
        utils.setCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea', false);

        // Create our own input area for interaction with the user.
        var cellInputNode = cell.input[0],
            kbaseNode,
            ui = UI.make({ node: cellInputNode });

        kbaseNode = ui.createNode(div({
            dataSubareaType: 'data-cell-input'
        }));

        cellInputNode.appendChild(kbaseNode);

        var dataCell = DataCell.make({
            cell: cell
        });
        dataCell.bus.emit('run', {
            node: kbaseNode
        });

        // The output cell just needs to inhibit the input area.
        // The input code and associated output (a widget) is already
        // to be found in this cell (during insertion).

        jupyter.disableKeyListenersForCell(cell);
        cell.renderMinMax();
        cell.renderIcon();
    }

    function upgradeCell(cell, setupData) {
        return Promise.try(function() {
            var meta = cell.metadata,
                cellId = setupData.cellId || (new Uuid(4).format());

            // Set the initial metadata for the output cell.
            meta.kbase = {
                type: 'data',
                attributes: {
                    id: cellId,
                    status: 'new',
                    created: new Date().toGMTString(),
                    lastLoaded: new Date().toGMTString(),
                    icon: 'database',
                    title: 'Data Cell'
                },
                dataCell: {
                    objectInfo: setupData.objectInfo,
                    widget: setupData.widget
                }
            };
            cell.metadata = meta;

            // We just need to generate, set, and execute the output
            // the first time (for now).

            var tag = Jupyter.narrative.sidePanel.$methodsWidget.currentTag;
            if (!tag) {
                tag = 'release';
            }
            var objInfo = setupData.objectInfo;
            var ref = objInfo.ref_path;
            var wsId = objInfo.ws_id || objInfo.wsid;
            if (!ref) {
                ref = wsId + '/' + objInfo.id + '/' + objInfo.version;
            }
            var title = (objInfo && objInfo.name) ? objInfo.name : 'Data Viewer';
            var cellText = PythonInterop.buildDataWidgetRunner(ref, cellId, title, tag);

            cell.set_text(cellText);
            cell.execute();

            // all we do for now is set up the input area
            utils.setCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea', false);

            utils.setCellMeta(cell, 'kbase.attributes.title', setupData.objectInfo.name);
            var subtitle = 'v' + String(setupData.objectInfo.version) + ' - ' + setupData.objectInfo.type;
            utils.setCellMeta(cell, 'kbase.attributes.subtitle', subtitle, true);

            setupCell(cell);
        });
    }

    function initializeExtension() {
        $([Jupyter.events]).on('insertedAtIndex.Cell', function(event, payload) {
            var cell = payload.cell;
            var setupData = payload.data;
            var jupyterCellType = payload.type;

            if (jupyterCellType === 'code' &&
                setupData &&
                setupData.type === 'data') {
                upgradeCell(cell, setupData)
                    .catch(function(err) {
                        console.error('ERROR creating cell', err);
                        // delete cell.
                        $(document).trigger('deleteCell.Narrative', Jupyter.notebook.find_cell_index(cell));
                        alert('Could not insert cell due to errors.\n' + err.message);
                    });
            }
        });

        Jupyter.notebook.get_cells().forEach(function(cell) {
            try {
                setupCell(cell);
            } catch (ex) {
                console.error('ERROR setting up output cell', ex);
            }
        });
    }

    function load() {
        /* Only initialize after the notebook is fully loaded. */
        if (Jupyter.notebook._fully_loaded) {
            initializeExtension();
        }
        else {
            $([Jupyter.events]).one('notebook_loaded.Notebook', function () {
                initializeExtension();
            });
        }
    }

    return {
        // This is the sole ipython/jupyter api call
        load_ipython_extension: load
    };
}, function(err) {
    console.error('ERROR loading dataCell main', err);
});
<commit_msg>minor make-lint-happy change and TODOs for data cell [SCT-804]<commit_after>/*global define*/
/*jslint white:true,browser:true*/

define([
    'bluebird',
    'jquery',
    'uuid',
    'base/js/namespace',
    'common/utils',
    'common/appUtils',
    'common/props',
    'common/cellUtils',
    'common/pythonInterop',
    'common/ui',
    'common/jupyter',
    'kb_common/html',
    './widgets/dataCell',
    'custom/custom'
], function(
    Promise,
    $,
    Uuid,
    Jupyter,
    utils,
    AppUtils,
    Props,
    cellUtils,
    PythonInterop,
    UI,
    jupyter,
    html,
    DataCell
) {
    'use strict';

    var t = html.tag,
        div = t('div');

    function specializeCell(cell) {
        cell.minimize = function() {
            var inputArea = this.input.find('.input_area').get(0),
                outputArea = this.element.find('.output_wrapper'),
                showCode = utils.getCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea');

            if (showCode) {
                inputArea.classList.remove('-show');
            }
            outputArea.addClass('hidden');
        };

        cell.maximize = function() {
            var inputArea = this.input.find('.input_area').get(0),
                outputArea = this.element.find('.output_wrapper'),
                showCode = utils.getCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea');

            if (showCode) {
                if (!inputArea.classList.contains('-show')) {
                    inputArea.classList.add('-show');
                    cell.code_mirror.refresh();
                }
            }
            outputArea.removeClass('hidden');
        };

        /*
         * The data cell icon is derived by looking up the type in the
         * narrative configuration.
         */
        cell.renderIcon = function() {
            var inputPrompt = this.element[0].querySelector('[data-element="icon"]'),
                type = Props.getDataItem(cell.metadata, 'kbase.dataCell.objectInfo.type');

            if (inputPrompt) {
                inputPrompt.innerHTML = div({
                    style: { textAlign: 'center' }
                }, [
                    AppUtils.makeTypeIcon(type)
                ]);
            }
        };

        cell.getIcon = function() {
            var type = Props.getDataItem(cell.metadata, 'kbase.dataCell.objectInfo.type'),
                icon = AppUtils.makeToolbarTypeIcon(type);
            return icon;
        };

        cell.toggleCodeInputArea = function() {
            var codeInputArea = this.input.find('.input_area')[0];
            if (codeInputArea) {
                codeInputArea.classList.toggle('-show');
                utils.setCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea', this.isCodeShowing(), true);
                // NB purely for side effect - toolbar refresh
                cell.metadata = cell.metadata;
            }
        };
    }

    function setupCell(cell) {
        if (cell.cell_type !== 'code') {
            return;
        }
        if (!cell.metadata.kbase) {
            return;
        }
        if (cell.metadata.kbase.type !== 'data') {
            return;
        }

        specializeCell(cell);

        // The kbase property is only used for managing runtime state of the cell
        // for kbase. Anything to be persistent should be on the metadata.
        cell.kbase = {};

        // Update metadata.
        utils.setMeta(cell, 'attributes', 'lastLoaded', (new Date()).toUTCString());

        // Ensure code showing is closed to start with.
        // Disable this line to allow this setting to be sticky.
        utils.setCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea', false);

        // Create our own input area for interaction with the user.
        var cellInputNode = cell.input[0],
            kbaseNode,
            ui = UI.make({ node: cellInputNode });

        kbaseNode = ui.createNode(div({
            dataSubareaType: 'data-cell-input'
        }));

        cellInputNode.appendChild(kbaseNode);

        var dataCell = DataCell.make({
            cell: cell
        });
        dataCell.bus.emit('run', {
            node: kbaseNode
        });

        // The output cell just needs to inhibit the input area.
        // The input code and associated output (a widget) is already
        // to be found in this cell (during insertion).

        jupyter.disableKeyListenersForCell(cell);
        cell.renderMinMax();
        cell.renderIcon();
    }

    function upgradeCell(cell, setupData) {
        return Promise.try(function() {
            var meta = cell.metadata,
                cellId = setupData.cellId || (new Uuid(4).format());

            // Set the initial metadata for the output cell.
            meta.kbase = {
                type: 'data',
                attributes: {
                    id: cellId,
                    status: 'new',
                    created: new Date().toGMTString(),
                    lastLoaded: new Date().toGMTString(),
                    icon: 'database',
                    title: 'Data Cell'
                },
                dataCell: {
                    objectInfo: setupData.objectInfo,
                    widget: setupData.widget
                }
            };
            cell.metadata = meta;

            // We just need to generate, set, and execute the output
            // the first time (for now).

            var tag = Jupyter.narrative.sidePanel.$methodsWidget.currentTag;
            if (!tag) {
                tag = 'release';
            }
            var objInfo = setupData.objectInfo;
            var ref = objInfo.ref_path;
            var wsId = objInfo.ws_id || objInfo.wsid;
            if (!ref) {
                ref = wsId + '/' + objInfo.id + '/' + objInfo.version;
            }
            var title = (objInfo && objInfo.name) ? objInfo.name : 'Data Viewer';
            var cellText = PythonInterop.buildDataWidgetRunner(ref, cellId, title, tag);

            cell.set_text(cellText);
            cell.execute();

            // all we do for now is set up the input area
            utils.setCellMeta(cell, 'kbase.dataCell.user-settings.showCodeInputArea', false);

            utils.setCellMeta(cell, 'kbase.attributes.title', setupData.objectInfo.name);
            var subtitle = 'v' + String(setupData.objectInfo.version) + ' - ' + setupData.objectInfo.type;
            utils.setCellMeta(cell, 'kbase.attributes.subtitle', subtitle, true);

            setupCell(cell);
        });
    }

    function initializeExtension() {
        $([Jupyter.events]).on('insertedAtIndex.Cell', function(event, payload) {
            var cell = payload.cell;
            var setupData = payload.data;
            var jupyterCellType = payload.type;

            if (jupyterCellType === 'code' &&
                setupData &&
                setupData.type === 'data') {
                upgradeCell(cell, setupData)
                    .catch(function(err) {
                        console.error('ERROR creating cell', err);
                        // delete cell.
                        $(document).trigger('deleteCell.Narrative', Jupyter.notebook.find_cell_index(cell));
                        alert('Could not insert cell due to errors.\n' + err.message);
                    });
            }
        });

        Jupyter.notebook.get_cells().forEach(function(cell) {
            try {
                setupCell(cell);
            } catch (ex) {
                console.error('ERROR setting up output cell', ex);
            }
        });
    }

    function load() {
        /* Only initialize after the notebook is fully loaded. */
        if (Jupyter.notebook._fully_loaded) {
            initializeExtension();
        }
        else {
            $([Jupyter.events]).one('notebook_loaded.Notebook', function () {
                initializeExtension();
            });
        }
    }

    return {
        // This is the sole ipython/jupyter api call
        load_ipython_extension: load
    };
}, function(err) {
    'use strict';
    // NB we should probably not be handling individual loading errors. If the
    // data cell couldn't load it is not recoverable -- the user should either reload the
    // narrative or the system is broken.
    console.error('ERROR loading dataCell main', err);
});
<|endoftext|>


<commit_before>(function () {
  const form = document.querySelector('form')
  const input = form.querySelector('input[name="url"]')

  form.addEventListener('submit', async (e) => {
    e.preventDefault()

    short.style.visibility = 'hidden'
    short.value = ''

    const response = await window.fetch('/', {
      method: 'POST',
      mode: 'same-origin',
      cache: 'no-cache',
      headers: new window.Headers({
        'Content-Type': 'application/json'
      }),
      body: JSON.stringify({
        url: input.value
      })
    })

    const json = await response.json()
    const short = form.querySelector('input[name="short"]')

    input.value = ''

    short.style.visibility = 'visible'
    short.value = json.short
    short.select()
  })
})()
<commit_msg>fix client-side js<commit_after>(function () {
  const form = document.querySelector('form')
  const url = form.querySelector('input[name="url"]')
  const short = form.querySelector('input[name="short"]')

  form.addEventListener('submit', async (e) => {
    e.preventDefault()

    short.style.visibility = 'hidden'
    short.value = ''

    const response = await window.fetch('/', {
      method: 'POST',
      mode: 'same-origin',
      cache: 'no-cache',
      headers: new window.Headers({
        'Content-Type': 'application/json'
      }),
      body: JSON.stringify({
        url: url.value
      })
    })

    const json = await response.json()

    url.value = ''

    short.style.visibility = 'visible'
    short.value = json.short
    short.select()
  })
})()
<|endoftext|>