<commit_before>/* eslint-disable no-underscore-dangle */

import { SheetsRegistry } from 'jss';
import { createMuiTheme, createGenerateClassName } from '@material-ui/core/styles';
import green from '@material-ui/core/colors/green';
import blue from '@material-ui/core/colors/blue';

function getTheme(uiTheme) {
  return createMuiTheme({
    nprogress: { color: uiTheme.paletteType === 'light' ? '#000' : '#fff' },
    palette: { ...uiTheme.paletteColors, type: uiTheme.paletteType },
    typography: { useNextVariants: true },
  });
}

const defaultTheme = {
  // paletteType: 'light',
  paletteColors: {
    primary: {
      main: '#1e88e5',
    },
    secondary: {
      main: '#008a00',
    },
  },
};

function createPageContext(paletteType) {
  const theme = getTheme({ ...defaultTheme, paletteType });

  return {
    theme,
    // This is needed in order to deduplicate the injection of CSS in the page.
    sheetsManager: new Map(),
    // This is needed in order to inject the critical CSS.
    sheetsRegistry: new SheetsRegistry(),
    // The standard class name generator.
    generateClassName: createGenerateClassName(),
  };
}

export function updatePageContext(paletteType) {
  const { paletteColors } = defaultTheme;

  const pageContext = {
    ...global.__MUI_PAGE_CONTEXT__,
    theme: getTheme({ paletteType, paletteColors }),
  };
  global.__MUI_PAGE_CONTEXT__ = pageContext;

  return pageContext;
}

export default function getPageContext(paletteType) {
  // Make sure to create a new context for every server-side request so that data
  // isn't shared between connections (which would be bad).
  if (!process.browser) {
    return createPageContext(paletteType);
  }

  // Reuse context on the client-side.
  if (!global.__INIT_MATERIAL_UI__) {
    global.__INIT_MATERIAL_UI__ = createPageContext(paletteType);
  }

  return global.__INIT_MATERIAL_UI__;
}
<commit_msg>Fix page context global variable name in getPageContext<commit_after>/* eslint-disable no-underscore-dangle */

import { SheetsRegistry } from 'jss';
import { createMuiTheme, createGenerateClassName } from '@material-ui/core/styles';
import green from '@material-ui/core/colors/green';
import blue from '@material-ui/core/colors/blue';

function getTheme(uiTheme) {
  return createMuiTheme({
    nprogress: { color: uiTheme.paletteType === 'light' ? '#000' : '#fff' },
    palette: { ...uiTheme.paletteColors, type: uiTheme.paletteType },
    typography: { useNextVariants: true },
  });
}

const defaultTheme = {
  // paletteType: 'light',
  paletteColors: {
    primary: {
      main: '#1e88e5',
    },
    secondary: {
      main: '#008a00',
    },
  },
};

function createPageContext(paletteType) {
  const theme = getTheme({ ...defaultTheme, paletteType });

  return {
    theme,
    // This is needed in order to deduplicate the injection of CSS in the page.
    sheetsManager: new Map(),
    // This is needed in order to inject the critical CSS.
    sheetsRegistry: new SheetsRegistry(),
    // The standard class name generator.
    generateClassName: createGenerateClassName(),
  };
}

export function updatePageContext(paletteType) {
  const { paletteColors } = defaultTheme;

  const pageContext = {
    ...global.__MUI_PAGE_CONTEXT__,
    theme: getTheme({ paletteType, paletteColors }),
  };
  global.__MUI_PAGE_CONTEXT__ = pageContext;

  return pageContext;
}

export default function getPageContext(paletteType) {
  // Make sure to create a new context for every server-side request so that data
  // isn't shared between connections (which would be bad).
  if (!process.browser) {
    return createPageContext(paletteType);
  }

  // Reuse context on the client-side.
  if (!global.__MUI_PAGE_CONTEXT__) {
    global.__MUI_PAGE_CONTEXT__ = createPageContext(paletteType);
  }

  return global.__MUI_PAGE_CONTEXT__;
}
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import classnames from 'classnames'
import * as pressureHelper from '~client/mobilizations/widgets/utils/pressure-helper'
import { isValidEmail, isValidPhoneE164 } from '~client/utils/validation-helper'
import AnalyticsEvents from '~client/mobilizations/widgets/utils/analytics-events'

if (require('exenv').canUseDOM) {
  require('./index.scss')
  require('./phone-calls.scss')
}

// TODO: Reusable Input
const controlClassname = 'px3 py1'
const inputReset = {
  border: 'none',
  padding: '0',
  height: '2rem',
  outline: 'none'
}

const parseTarget = target => {
  const targetSplit = target.split('<')
  const valid = targetSplit.length === 2
  return valid ? { name: targetSplit[0].trim(), value: targetSplit[1].replace('>', '') } : null
}

class RealtimeCallDuration extends Component {
  constructor (props) {
    super(props)
    this.state = { duration: 0, interval: undefined }
  }

  timer = () => {
    this.setState({ duration: this.state.duration + 1 })
  }

  componentDidMount() {
    this.setState({ interval: setInterval(this.timer, 1000) })
  }

  componentWillUnmount() {
    clearInterval(this.state.interval)
  }

  render () {
    return {`${this.state.duration}s`}
  }
}

class PressureForm extends Component {
  constructor (props) {
    super(props)
    this.state = {
      email: '',
      phone: '',
      name: '',
      lastname: '',
      city: '',
      subject: props.subject,
      body: props.body,
      pressureType: pressureHelper.getType(props.targetList) || undefined,
      callManagement: undefined
    }
  }

  componentWillReceiveProps(props) {
    if (!this.props.callTransition && props.targetList && props.targetList.length) {
      this.setState({
        callManagement: props.targetList.map(target => ({
          ...parseTarget(target),
          attempts: 0
        }))
      })
    }

    if (
      props.callTransition &&
      props.targetList &&
      props.targetList.length &&
      JSON.stringify(this.props.callTransition) !== JSON.stringify(props.callTransition)
    ) {
      this.setState({
        callManagement: this.state.callManagement.map((target, index) => {
          const isCallToCurrentTarget = target.value === props.callTransition.twilioCallTo
          const transition = isCallToCurrentTarget ? props.callTransition : {}
          const { twilioCallTransitionStatus: status } = transition
          const isFailStatus = ['busy', 'failed', 'no-answer'].includes(status)

          let attempts = this.state.callManagement[index].attempts
          if (isCallToCurrentTarget && isFailStatus) attempts++

          return { ...target, ...transition, attempts }
        })
      })
    }
  }

  validate () {
    const { targetList, widget: { settings: { show_city: showCity } } } = this.props
    const requiredMsg = 'Preenchimento obrigatório'
    const errors = { valid: true }

    if (this.state.pressureType === pressureHelper.PRESSURE_TYPE_EMAIL) {
      if (!this.state.email) {
        errors.email = requiredMsg
      } else if (!isValidEmail(this.state.email)) {
        errors.email = 'E-mail inválido'
      } else if (targetList && targetList.some(target => target.match(`<${this.state.email}>`))) {
        errors.email = 'O email que você está tentando usar é de um dos alvos da mobilização.'
      }
      if (!this.state.subject) {
        errors.subject = requiredMsg
      }
      if (!this.state.body) {
        errors.body = requiredMsg
      }
    }
    if (this.state.pressureType === pressureHelper.PRESSURE_TYPE_PHONE) {
      const { phone } = this.state
      const phoneE164 = /^\+/.test(phone) ? phone : `+${phone}`
      if (!phone) {
        errors.phone = requiredMsg
      } else if (!isValidPhoneE164(phoneE164)) {
        if ([11, 12].includes(phoneE164.length)) {
          errors.phone = 'Informe o código do país e o DDD com dois dígitos. Ex: +5511'
        } else {
          errors.phone = 'Telefone inválido'
        }
      } else if (
        targetList &&
        targetList.some(
          target => target.replace(/\D/g, '')
            .match(`${this.state.phone.replace(/\D/g, '')}`)
        )
      ) {
        errors.phone = 'O telefone que você está tentando usar é de um dos alvos da mobilização.'
      }
    }
    if (!this.state.name) {
      errors.name = requiredMsg
    }
    if (!this.state.lastname) {
      errors.lastname = requiredMsg
    }
    if (showCity === 'city-true' && !this.state.city) {
      errors.city = requiredMsg
    }

    if (Object.keys(errors).length > 1) {
      errors.valid = false
    }
    return errors
  }

  handleSubmit (e) {
    e.preventDefault()
    const { onSubmit } = this.props
    const errors = this.validate()

    this.setState({ errors })
    if (errors.valid) onSubmit && onSubmit(this.state)
  }

  render () {
    const {
      targetList,
      callTransition,
      buttonColor,
      buttonText,
      children,
      widget,
      disabled,
      addTwilioCallMutation
    } = this.props
    const {
      email, phone, name, lastname, city, subject, body, errors,
      callManagement
    } = this.state

    return (
      <form
        className={classnames(
          'pressure-form',
          { 'is-calling': !!callTransition }
        )}
        onSubmit={::this.handleSubmit}
      >

            {this.state.pressureType === 'email' && (

                  E-mail
                  {(errors && errors['email'] && {errors['email']})}

                <input
                  id='pressure-sender-email-id'
                  className='col-12'
                  style={inputReset}
                  onBlur={::AnalyticsEvents.pressureIsFilled}
                  type='email'
                  placeholder='Insira seu e-mail'
                  value={email}
                  onChange={e => this.setState({ email: e.target.value })}
                />

            )}
            {this.state.pressureType === 'phone' && (

                  Telefone
                  {(errors && errors['phone'] && {errors['phone']})}

                <input
                  id='pressure-sender-phone-id'
                  className='col-12'
                  style={inputReset}
                  onBlur={::AnalyticsEvents.pressureIsFilled}
                  type='text'
                  placeholder='Insira seu telefone. Ex: +5511987654321'
                  value={phone}
                  onChange={e => this.setState({ phone: e.target.value })}
                />

            )}

                Nome
                {(errors && errors['name'] && {errors['name']})}

              <input
                id='pressure-sender-firstname-id'
                className='col-12'
                style={inputReset}
                type='text'
                placeholder='Insira seu nome'
                value={name}
                onChange={e => this.setState({ name: e.target.value })}
              />

                Sobrenome
                {(errors && errors['lastname'] && {errors['lastname']})}

              <input
                id='pressure-sender-lastname-id'
                className='col-12'
                style={inputReset}
                type='text'
                placeholder='Insira seu sobrenome'
                value={lastname}
                onChange={e => this.setState({ lastname: e.target.value })}
              />

            {
              !widget.settings.show_city || widget.settings.show_city !== 'city-true' ? null : (

                    Cidade
                    {(errors && errors['city'] && {errors['city']})}

                  <input
                    className='col-12'
                    style={inputReset}
                    type='text'
                    placeholder='Insira sua cidade'
                    value={city}
                    onChange={e => this.setState({ city: e.target.value })}
                  />

              )
            }
            {this.state.pressureType === 'email' && (

                  Assunto
                  {(errors && errors['subject'] && {errors['subject']})}

                <input
                  id='pressure-subject-id'
                  className='col-12'
                  style={inputReset}
                  type='text'
                  value={subject}
                  disabled={disabled}
                  onChange={e => this.setState({ subject: e.target.value })}
                />

            )}
            {this.state.pressureType === 'email' && (

                  Corpo do e-mail
                  {(errors && errors['body'] && {errors['body']})}

                <textarea
                  id='pressure-body-id'
                  className='col-12 mt1'
                  style={{...inputReset, height: '7rem'}}
                  value={body}
                  disabled={disabled}
                  onChange={e => this.setState({ body: e.target.value })}
                />

            )}

            <button
              type='submit'
              onClick={::this.handleSubmit}
              className='btn caps white col-12 py2 rounded'
              style={{ backgroundColor: buttonColor }}
            >
              {buttonText}
            </button>

Ligações

            {callManagement && callManagement.length && callManagement.map(target => {
              const {
                name,
                value,
                attempts,
                twilioCallTo: to,
                twilioCallTransitionStatus: status,
                twilioCallTransitionCallDuration: duration
              } = target
              let ListItem =

              if (to === value) {
                if (status === 'completed') {
                  ListItem = (

{name}

                        {duration}s

                  )
                } else if (['initiated', 'ringing', 'in-progress'].includes(status)) {
                  ListItem = (

{name}

                          {status === 'in-progress' && <RealtimeCallDuration />}

                        <button className='btn-call calling'>
                          {status === 'initiated' && 'Iniciando'}
                          {status === 'ringing' && 'Ligando...'}
                          {status === 'in-progress' && 'Conectado'}
                        </button>

                  )
                } else if (['busy', 'failed', 'no-answer'].includes(status)) {
                  ListItem = (

{name}

                        3x

                  )
                  if (attempts < 3) {
                    ListItem = (

{name}

                            {attempts}x

                          <button
                            className='btn-call outlined'
                            onClick={e => {
                              e.preventDefault()
                              addTwilioCallMutation({
                                widgetId: this.props.widget.id,
                                from: this.state.phone,
                                to: value
                              })
                            }}
                          >
                            Religar
                          </button>

                    )
                  }
                }
              } else {
                ListItem = (

{name}

                    <button
                      className='btn-call primary'
                      type='button'
                      onClick={e => {
                        e.preventDefault()
                        addTwilioCallMutation({
                          widgetId: this.props.widget.id,
                          from: this.state.phone,
                          to: value
                        })
                      }}
                    >
                      Ligar
                    </button>

                )
              }
              return ListItem
            })}

              Sucesso

              Religar (até 3x)

              Em andamento

              Disponível

              Erro

            <button
              type='button'
              className='btn-call outlined full-width'
              onClick={e => {
                e.preventDefault()
                const { changeParentState } = this.props
                const message = 'Tem certeza que deseja finalizar a pressão por telefone?'
                window.confirm(message) && changeParentState({ showFinishMessage: true })
              }}
            >
              Finalizar pressão por telefone
            </button>

        {children}
      </form>
    )
  }
}

PressureForm.propTypes = {
  onSubmit: PropTypes.func,
  buttonColor: PropTypes.string,
  buttonText: PropTypes.string,
  subject: PropTypes.string,
  body: PropTypes.string,
  widget: PropTypes.object,
  changeParentState: PropTypes.func.isRequired
}

PressureForm.defaultProps = {
  subject: '',
  body: '',
  changeParentState: () => {}
}

export default PressureForm
<commit_msg>chore(phone-pressure): remove confirm msg; change btn text #817<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import classnames from 'classnames'
import * as pressureHelper from '~client/mobilizations/widgets/utils/pressure-helper'
import { isValidEmail, isValidPhoneE164 } from '~client/utils/validation-helper'
import AnalyticsEvents from '~client/mobilizations/widgets/utils/analytics-events'

if (require('exenv').canUseDOM) {
  require('./index.scss')
  require('./phone-calls.scss')
}

// TODO: Reusable Input
const controlClassname = 'px3 py1'
const inputReset = {
  border: 'none',
  padding: '0',
  height: '2rem',
  outline: 'none'
}

const parseTarget = target => {
  const targetSplit = target.split('<')
  const valid = targetSplit.length === 2
  return valid ? { name: targetSplit[0].trim(), value: targetSplit[1].replace('>', '') } : null
}

class RealtimeCallDuration extends Component {
  constructor (props) {
    super(props)
    this.state = { duration: 0, interval: undefined }
  }

  timer = () => {
    this.setState({ duration: this.state.duration + 1 })
  }

  componentDidMount() {
    this.setState({ interval: setInterval(this.timer, 1000) })
  }

  componentWillUnmount() {
    clearInterval(this.state.interval)
  }

  render () {
    return {`${this.state.duration}s`}
  }
}

class PressureForm extends Component {
  constructor (props) {
    super(props)
    this.state = {
      email: '',
      phone: '',
      name: '',
      lastname: '',
      city: '',
      subject: props.subject,
      body: props.body,
      pressureType: pressureHelper.getType(props.targetList) || undefined,
      callManagement: undefined
    }
  }

  componentWillReceiveProps(props) {
    if (!this.props.callTransition && props.targetList && props.targetList.length) {
      this.setState({
        callManagement: props.targetList.map(target => ({
          ...parseTarget(target),
          attempts: 0
        }))
      })
    }

    if (
      props.callTransition &&
      props.targetList &&
      props.targetList.length &&
      JSON.stringify(this.props.callTransition) !== JSON.stringify(props.callTransition)
    ) {
      this.setState({
        callManagement: this.state.callManagement.map((target, index) => {
          const isCallToCurrentTarget = target.value === props.callTransition.twilioCallTo
          const transition = isCallToCurrentTarget ? props.callTransition : {}
          const { twilioCallTransitionStatus: status } = transition
          const isFailStatus = ['busy', 'failed', 'no-answer'].includes(status)

          let attempts = this.state.callManagement[index].attempts
          if (isCallToCurrentTarget && isFailStatus) attempts++

          return { ...target, ...transition, attempts }
        })
      })
    }
  }

  validate () {
    const { targetList, widget: { settings: { show_city: showCity } } } = this.props
    const requiredMsg = 'Preenchimento obrigatório'
    const errors = { valid: true }

    if (this.state.pressureType === pressureHelper.PRESSURE_TYPE_EMAIL) {
      if (!this.state.email) {
        errors.email = requiredMsg
      } else if (!isValidEmail(this.state.email)) {
        errors.email = 'E-mail inválido'
      } else if (targetList && targetList.some(target => target.match(`<${this.state.email}>`))) {
        errors.email = 'O email que você está tentando usar é de um dos alvos da mobilização.'
      }
      if (!this.state.subject) {
        errors.subject = requiredMsg
      }
      if (!this.state.body) {
        errors.body = requiredMsg
      }
    }
    if (this.state.pressureType === pressureHelper.PRESSURE_TYPE_PHONE) {
      const { phone } = this.state
      const phoneE164 = /^\+/.test(phone) ? phone : `+${phone}`
      if (!phone) {
        errors.phone = requiredMsg
      } else if (!isValidPhoneE164(phoneE164)) {
        if ([11, 12].includes(phoneE164.length)) {
          errors.phone = 'Informe o código do país e o DDD com dois dígitos. Ex: +5511'
        } else {
          errors.phone = 'Telefone inválido'
        }
      } else if (
        targetList &&
        targetList.some(
          target => target.replace(/\D/g, '')
            .match(`${this.state.phone.replace(/\D/g, '')}`)
        )
      ) {
        errors.phone = 'O telefone que você está tentando usar é de um dos alvos da mobilização.'
      }
    }
    if (!this.state.name) {
      errors.name = requiredMsg
    }
    if (!this.state.lastname) {
      errors.lastname = requiredMsg
    }
    if (showCity === 'city-true' && !this.state.city) {
      errors.city = requiredMsg
    }

    if (Object.keys(errors).length > 1) {
      errors.valid = false
    }
    return errors
  }

  handleSubmit (e) {
    e.preventDefault()
    const { onSubmit } = this.props
    const errors = this.validate()

    this.setState({ errors })
    if (errors.valid) onSubmit && onSubmit(this.state)
  }

  render () {
    const {
      targetList,
      callTransition,
      buttonColor,
      buttonText,
      children,
      widget,
      disabled,
      addTwilioCallMutation
    } = this.props
    const {
      email, phone, name, lastname, city, subject, body, errors,
      callManagement
    } = this.state

    return (
      <form
        className={classnames(
          'pressure-form',
          { 'is-calling': !!callTransition }
        )}
        onSubmit={::this.handleSubmit}
      >

            {this.state.pressureType === 'email' && (

                  E-mail
                  {(errors && errors['email'] && {errors['email']})}

                <input
                  id='pressure-sender-email-id'
                  className='col-12'
                  style={inputReset}
                  onBlur={::AnalyticsEvents.pressureIsFilled}
                  type='email'
                  placeholder='Insira seu e-mail'
                  value={email}
                  onChange={e => this.setState({ email: e.target.value })}
                />

            )}
            {this.state.pressureType === 'phone' && (

                  Telefone
                  {(errors && errors['phone'] && {errors['phone']})}

                <input
                  id='pressure-sender-phone-id'
                  className='col-12'
                  style={inputReset}
                  onBlur={::AnalyticsEvents.pressureIsFilled}
                  type='text'
                  placeholder='Insira seu telefone. Ex: +5511987654321'
                  value={phone}
                  onChange={e => this.setState({ phone: e.target.value })}
                />

            )}

                Nome
                {(errors && errors['name'] && {errors['name']})}

              <input
                id='pressure-sender-firstname-id'
                className='col-12'
                style={inputReset}
                type='text'
                placeholder='Insira seu nome'
                value={name}
                onChange={e => this.setState({ name: e.target.value })}
              />

                Sobrenome
                {(errors && errors['lastname'] && {errors['lastname']})}

              <input
                id='pressure-sender-lastname-id'
                className='col-12'
                style={inputReset}
                type='text'
                placeholder='Insira seu sobrenome'
                value={lastname}
                onChange={e => this.setState({ lastname: e.target.value })}
              />

            {
              !widget.settings.show_city || widget.settings.show_city !== 'city-true' ? null : (

                    Cidade
                    {(errors && errors['city'] && {errors['city']})}

                  <input
                    className='col-12'
                    style={inputReset}
                    type='text'
                    placeholder='Insira sua cidade'
                    value={city}
                    onChange={e => this.setState({ city: e.target.value })}
                  />

              )
            }
            {this.state.pressureType === 'email' && (

                  Assunto
                  {(errors && errors['subject'] && {errors['subject']})}

                <input
                  id='pressure-subject-id'
                  className='col-12'
                  style={inputReset}
                  type='text'
                  value={subject}
                  disabled={disabled}
                  onChange={e => this.setState({ subject: e.target.value })}
                />

            )}
            {this.state.pressureType === 'email' && (

                  Corpo do e-mail
                  {(errors && errors['body'] && {errors['body']})}

                <textarea
                  id='pressure-body-id'
                  className='col-12 mt1'
                  style={{...inputReset, height: '7rem'}}
                  value={body}
                  disabled={disabled}
                  onChange={e => this.setState({ body: e.target.value })}
                />

            )}

            <button
              type='submit'
              onClick={::this.handleSubmit}
              className='btn caps white col-12 py2 rounded'
              style={{ backgroundColor: buttonColor }}
            >
              {buttonText}
            </button>

Ligações

            {callManagement && callManagement.length && callManagement.map(target => {
              const {
                name,
                value,
                attempts,
                twilioCallTo: to,
                twilioCallTransitionStatus: status,
                twilioCallTransitionCallDuration: duration
              } = target
              let ListItem =

              if (to === value) {
                if (status === 'completed') {
                  ListItem = (

{name}

                        {duration}s

                  )
                } else if (['initiated', 'ringing', 'in-progress'].includes(status)) {
                  ListItem = (

{name}

                          {status === 'in-progress' && <RealtimeCallDuration />}

                        <button className='btn-call calling'>
                          {status === 'initiated' && 'Iniciando'}
                          {status === 'ringing' && 'Ligando...'}
                          {status === 'in-progress' && 'Conectado'}
                        </button>

                  )
                } else if (['busy', 'failed', 'no-answer'].includes(status)) {
                  ListItem = (

{name}

                        3x

                  )
                  if (attempts < 3) {
                    ListItem = (

{name}

                            {attempts}x

                          <button
                            className='btn-call outlined'
                            onClick={e => {
                              e.preventDefault()
                              addTwilioCallMutation({
                                widgetId: this.props.widget.id,
                                from: this.state.phone,
                                to: value
                              })
                            }}
                          >
                            Religar
                          </button>

                    )
                  }
                }
              } else {
                ListItem = (

{name}

                    <button
                      className='btn-call primary'
                      type='button'
                      onClick={e => {
                        e.preventDefault()
                        addTwilioCallMutation({
                          widgetId: this.props.widget.id,
                          from: this.state.phone,
                          to: value
                        })
                      }}
                    >
                      Ligar
                    </button>

                )
              }
              return ListItem
            })}

              Sucesso

              Religar (até 3x)

              Em andamento

              Disponível

              Erro

            <button
              type='button'
              className='btn-call outlined full-width'
              onClick={e => {
                e.preventDefault()
                this.props.changeParentState({ showFinishMessage: true })
              }}
            >
              Encerrar e Compartilhar
            </button>

        {children}
      </form>
    )
  }
}

PressureForm.propTypes = {
  onSubmit: PropTypes.func,
  buttonColor: PropTypes.string,
  buttonText: PropTypes.string,
  subject: PropTypes.string,
  body: PropTypes.string,
  widget: PropTypes.object,
  changeParentState: PropTypes.func.isRequired
}

PressureForm.defaultProps = {
  subject: '',
  body: '',
  changeParentState: () => {}
}

export default PressureForm
<|endoftext|>


<commit_before>function xdiff_string_diff(old_data, new_data, context_lines, minimal) {
  // From: http://phpjs.org/functions
  // +   original by: Brett Zamir (http://brett-zamir.me)
  // +   based on: Imgen Tata (http://www.myipdf.com/)
  // +   bugfixed by: Imgen Tata (http://www.myipdf.com/)
  // +   improved by: Brett Zamir (http://brett-zamir.me)
  // %        note 1: The minimal argument is not currently supported
  // *     example 1: xdiff_string_diff('', 'Hello world!');
  // *     returns 1: '@@ -0,0 +1,1 @@\n+Hello world!'

  // (This code was done by Imgen Tata; I have only reformatted for use in php.js)

  // See http://en.wikipedia.org/wiki/Diff#Unified_format
  var i = 0,
      j = 0,
      k = 0,
      ori_hunk_start, new_hunk_start, ori_hunk_end, new_hunk_end, ori_hunk_line_no, new_hunk_line_no, ori_hunk_size, new_hunk_size,
      // Potential configuration
      MAX_CONTEXT_LINES = Number.POSITIVE_INFINITY,
      MIN_CONTEXT_LINES = 0,
      DEFAULT_CONTEXT_LINES = 3,
      //
      HEADER_PREFIX = '@@ ',
      HEADER_SUFFIX = ' @@',
      ORIGINAL_INDICATOR = '-',
      NEW_INDICATOR = '+',
      RANGE_SEPARATOR = ',',
      CONTEXT_INDICATOR = ' ',
      DELETION_INDICATOR = '-',
      ADDITION_INDICATOR = '+',
      ori_lines, new_lines, NEW_LINE = '\n',
      /**
      * Trims string
      */
      trim = function(text) {
        if (typeof text !== 'string') {
          throw new Error('String parameter required');
        }

        return text.replace(/(^\s*)|(\s*$)/g, '');
      },
      /**
      * Verifies type of arguments
      */
      verify_type = function(type) {
        var args = arguments,
                args_len = arguments.length,
                basic_types = ['number', 'boolean', 'string', 'function', 'object', 'undefined'],
                basic_type, i, j, type_of_type = typeof type;
        if (type_of_type !== 'string' && type_of_type !== 'function') {
          throw new Error('Bad type parameter');
        }

        if (args_len < 2) {
          throw new Error('Too few arguments');
        }

        if (type_of_type === 'string') {
          type = trim(type);

          if (type === '') {
            throw new Error('Bad type parameter');
          }

          for (j = 0; j < basic_types.length; j++) {
            basic_type = basic_types[j];

            if (basic_type == type) {
              for (i = 1; i < args_len; i++) {
                if (typeof args[i] !== type) {
                  throw new Error('Bad type');
                }
              }

              return;
            }
          }

          throw new Error('Bad type parameter');
        }

        // Not basic type. we need to use instanceof operator
        for (i = 1; i < args_len; i++) {
          if (!(args[i] instanceof type)) {
            throw new Error('Bad type');
          }
        }
      },
      /**
      * Checks if the specified array contains an element with specified value
      */
      has_value = function(array, value) {
        var i;
        verify_type(Array, array);

        for (i = 0; i < array.length; i++) {
          if (array[i] === value) {
            return true;
          }
        }

        return false;
      },
      /**
      * Checks the type of arguments
      * @param {String | Function} type Specifies the desired type
      * @return {Boolean} Return true if all arguments after the type argument are of specified type. Else false
      */
      are_type_of = function(type) {
        var args = arguments,
                args_len = arguments.length,
                basic_types = ['number', 'boolean', 'string', 'function', 'object', 'undefined'],
                basic_type, i, j, type_of_type = typeof type;
        if (type_of_type !== 'string' && type_of_type !== 'function') {
          throw new Error('Bad type parameter');
        }

        if (args_len < 2) {
          throw new Error('Too few arguments');
        }

        if (type_of_type === 'string') {
          type = trim(type);

          if (type === '') {
            return false;
          }

          for (j = 0; j < basic_types.length; j++) {
            basic_type = basic_types[j];

            if (basic_type == type) {
              for (i = 1; i < args_len; i++) {
                if (typeof args[i] != type) {
                  return false;
                }
              }

              return true;
            }
          }

          throw new Error('Bad type parameter');
        }

        // Not basic type. we need to use instanceof operator
        for (i = 1; i < args_len; i++) {
          if (!(args[i] instanceof type)) {
            return false;
          }
        }

        return true;
      },
      /*
      * Initialize and return an array with specified size and initial value
      */
      get_initialized_array = function(array_size, init_value) {
        var array = [],
                i;
        verify_type('number', array_size);

        for (i = 0; i < array_size; i++) {
          array.push(init_value);
        }

        return array;
      },
      /**
      * Splits text into lines and return as a string array
      */
      split_into_lines = function(text) {
        verify_type('string', text);

        if (text === '') {
          return [];
        }
        return text.split('\n');
      },
      is_empty_array = function(obj) {
        return are_type_of(Array, obj) && obj.length === 0;
      },
      /**
      * Finds longest common sequence between two sequences
      * @see {@link http://wordaligned.org/articles/longest-common-subsequence}
      */
      find_longest_common_sequence = function(seq1, seq2, seq1_is_in_lcs, seq2_is_in_lcs) {
        if (!are_type_of(Array, seq1, seq2)) {
          throw new Error('Array parameters are required');
        }

        // Deal with edge case
        if (is_empty_array(seq1) || is_empty_array(seq2)) {
          return [];
        }

        // Function to calculate lcs lengths
        var lcs_lens = function(xs, ys) {
          var i, j, prev,
            curr = get_initialized_array(ys.length + 1, 0);

          for (i = 0; i < xs.length; i++) {
            prev = curr.slice(0);
            for (j = 0; j < ys.length; j++) {
              if (xs[i] === ys[j]) {
                curr[j + 1] = prev[j] + 1;
              } else {
                curr[j + 1] = Math.max(curr[j], prev[j + 1]);
              }
            }
          }

          return curr;
        },
        // Function to find lcs and fill in the array to indicate the optimal longest common sequence
        find_lcs = function(xs, xidx, xs_is_in, ys) {
          var i, xb, xe, ll_b, ll_e, pivot, max, yb, ye,
            nx = xs.length,
            ny = ys.length;

          if (nx === 0) {
            return [];
          }
          if (nx === 1) {
            if (has_value(ys, xs[0])) {
              xs_is_in[xidx] = true;
              return [xs[0]];
            }
            return [];
          }
          i = Math.floor(nx / 2);
          xb = xs.slice(0, i);
          xe = xs.slice(i);
          ll_b = lcs_lens(xb, ys);
          ll_e = lcs_lens(xe.slice(0).reverse(), ys.slice(0).reverse());

          pivot = 0;
          max = 0;
          for (j = 0; j <= ny; j++) {
            if (ll_b[j] + ll_e[ny - j] > max) {
              pivot = j;
              max = ll_b[j] + ll_e[ny - j];
            }
          }
          yb = ys.slice(0, pivot);
          ye = ys.slice(pivot);
          return find_lcs(xb, xidx, xs_is_in, yb).concat(find_lcs(xe, xidx + i, xs_is_in, ye));
        };

        // Fill in seq1_is_in_lcs to find the optimal longest common subsequence of first sequence
        find_lcs(seq1, 0, seq1_is_in_lcs, seq2);
        // Fill in seq2_is_in_lcs to find the optimal longest common subsequence of second sequence and return the result
        return find_lcs(seq2, 0, seq2_is_in_lcs, seq1);
      };

  // First, check the parameters
  if (are_type_of('string', old_data, new_data) === false) {
    return false;
  }

  if (old_data == new_data) {
    return '';
  }

  if (typeof context_lines !== 'number' || context_lines > MAX_CONTEXT_LINES || context_lines < MIN_CONTEXT_LINES) {
    context_lines = DEFAULT_CONTEXT_LINES;
  }

  ori_lines = split_into_lines(old_data);
  new_lines = split_into_lines(new_data);
  var ori_len = ori_lines.length,
      new_len = new_lines.length,
      ori_is_in_lcs = get_initialized_array(ori_len, false),
      new_is_in_lcs = get_initialized_array(new_len, false),
      lcs_len = find_longest_common_sequence(ori_lines, new_lines, ori_is_in_lcs, new_is_in_lcs).length,
      unidiff = '';

  if (lcs_len === 0) { // No common sequence
    unidiff = HEADER_PREFIX + ORIGINAL_INDICATOR + (ori_len > 0 ? '1' : '0') + RANGE_SEPARATOR + ori_len + ' ' + NEW_INDICATOR + (new_len > 0 ? '1' : '0') + RANGE_SEPARATOR + new_len + HEADER_SUFFIX;

    for (i = 0; i < ori_len; i++) {
      unidiff += NEW_LINE + DELETION_INDICATOR + ori_lines[i];
    }

    for (j = 0; j < new_len; j++) {
      unidiff += NEW_LINE + ADDITION_INDICATOR + new_lines[j];
    }

    return unidiff;
  }

  var leading_context = [],
      trailing_context = [],
      actual_leading_context = [],
      actual_trailing_context = [],

      // Regularize leading context by the context_lines parameter
      regularize_leading_context = function(context) {
        if (context.length === 0 || context_lines === 0) {
          return [];
        }

        var context_start_pos = Math.max(context.length - context_lines, 0);

        return context.slice(context_start_pos);
      },

      // Regularize trailing context by the context_lines parameter
      regularize_trailing_context = function(context) {
        if (context.length === 0 || context_lines === 0) {
          return [];
        }

        return context.slice(0, Math.min(context_lines, context.length));
      };

  // Skip common lines in the beginning
  while (i < ori_len && ori_is_in_lcs[i] === true && new_is_in_lcs[i] === true) {
    leading_context.push(ori_lines[i]);
    i++;
  }

  j = i;
  k = i; // The index in the longest common sequence
  ori_hunk_start = i;
  new_hunk_start = j;
  ori_hunk_end = i;
  new_hunk_end = j;

  while (i < ori_len || j < new_len) {
    while (i < ori_len && ori_is_in_lcs[i] === false) {
      i++;
    }
    ori_hunk_end = i;

    while (j < new_len && new_is_in_lcs[j] === false) {
      j++;
    }
    new_hunk_end = j;

    // Find the trailing context
    trailing_context = [];
    while (i < ori_len && ori_is_in_lcs[i] === true && j < new_len && new_is_in_lcs[j] === true) {
      trailing_context.push(ori_lines[i]);
      k++;
      i++;
      j++;
    }

    if (k >= lcs_len || // No more in longest common lines
        trailing_context.length >= 2 * context_lines) { // Context break found
      if (trailing_context.length < 2 * context_lines) { // It must be last block of common lines but not a context break
        trailing_context = [];

        // Force break out
        i = ori_len;
        j = new_len;

        // Update hunk ends to force output to the end
        ori_hunk_end = ori_len;
        new_hunk_end = new_len;
      }

      // Output the diff hunk

      // Trim the leading and trailing context block
      actual_leading_context = regularize_leading_context(leading_context);
      actual_trailing_context = regularize_trailing_context(trailing_context);

      ori_hunk_start -= actual_leading_context.length;
      new_hunk_start -= actual_leading_context.length;
      ori_hunk_end += actual_trailing_context.length;
      new_hunk_end += actual_trailing_context.length;

      ori_hunk_line_no = ori_hunk_start + 1;
      new_hunk_line_no = new_hunk_start + 1;
      ori_hunk_size = ori_hunk_end - ori_hunk_start;
      new_hunk_size = new_hunk_end - new_hunk_start;

      // Build header
      unidiff += HEADER_PREFIX + ORIGINAL_INDICATOR + ori_hunk_line_no + RANGE_SEPARATOR + ori_hunk_size + ' ' + NEW_INDICATOR + new_hunk_line_no + RANGE_SEPARATOR + new_hunk_size + HEADER_SUFFIX + NEW_LINE;

      // Build the diff hunk content
      while (ori_hunk_start < ori_hunk_end || new_hunk_start < new_hunk_end) {
        if (ori_hunk_start < ori_hunk_end && ori_is_in_lcs[ori_hunk_start] === true && new_is_in_lcs[new_hunk_start] === true) { // The context line
          unidiff += CONTEXT_INDICATOR + ori_lines[ori_hunk_start] + NEW_LINE;
          ori_hunk_start++;
          new_hunk_start++;
        } else if (ori_hunk_start < ori_hunk_end && ori_is_in_lcs[ori_hunk_start] === false) { // The deletion line
          unidiff += DELETION_INDICATOR + ori_lines[ori_hunk_start] + NEW_LINE;
          ori_hunk_start++;
        } else if (new_hunk_start < new_hunk_end && new_is_in_lcs[new_hunk_start] === false) { // The additional line
          unidiff += ADDITION_INDICATOR + new_lines[new_hunk_start] + NEW_LINE;
          new_hunk_start++;
        }
      }

      // Update hunk position and leading context
      ori_hunk_start = i;
      new_hunk_start = j;
      leading_context = trailing_context;
    }
  }

  // Trim the trailing new line if it exists
  if (unidiff.length > 0 && unidiff.charAt(unidiff.length) === NEW_LINE) {
    unidiff = unidiff.slice(0, -1);
  }

  return unidiff;
}
<commit_msg>Cleanup headers<commit_after>function xdiff_string_diff(old_data, new_data, context_lines, minimal) {
  //  discuss at: http://phpjs.org/functions/xdiff_string_diff
  // original by: Brett Zamir (http://brett-zamir.me)
  //    based on: Imgen Tata (http://www.myipdf.com/)
  // bugfixed by: Imgen Tata (http://www.myipdf.com/)
  // improved by: Brett Zamir (http://brett-zamir.me)
  //        note: The minimal argument is not currently supported
  //   example 1: xdiff_string_diff('', 'Hello world!');
  //   returns 1: '@@ -0,0 +1,1 @@\n+Hello world!'

  // (This code was done by Imgen Tata; I have only reformatted for use in php.js)

  // See http://en.wikipedia.org/wiki/Diff#Unified_format
  var i = 0,
      j = 0,
      k = 0,
      ori_hunk_start, new_hunk_start, ori_hunk_end, new_hunk_end, ori_hunk_line_no, new_hunk_line_no, ori_hunk_size, new_hunk_size,
      // Potential configuration
      MAX_CONTEXT_LINES = Number.POSITIVE_INFINITY,
      MIN_CONTEXT_LINES = 0,
      DEFAULT_CONTEXT_LINES = 3,
      //
      HEADER_PREFIX = '@@ ',
      HEADER_SUFFIX = ' @@',
      ORIGINAL_INDICATOR = '-',
      NEW_INDICATOR = '+',
      RANGE_SEPARATOR = ',',
      CONTEXT_INDICATOR = ' ',
      DELETION_INDICATOR = '-',
      ADDITION_INDICATOR = '+',
      ori_lines, new_lines, NEW_LINE = '\n',
      /**
      * Trims string
      */
      trim = function(text) {
        if (typeof text !== 'string') {
          throw new Error('String parameter required');
        }

        return text.replace(/(^\s*)|(\s*$)/g, '');
      },
      /**
      * Verifies type of arguments
      */
      verify_type = function(type) {
        var args = arguments,
                args_len = arguments.length,
                basic_types = ['number', 'boolean', 'string', 'function', 'object', 'undefined'],
                basic_type, i, j, type_of_type = typeof type;
        if (type_of_type !== 'string' && type_of_type !== 'function') {
          throw new Error('Bad type parameter');
        }

        if (args_len < 2) {
          throw new Error('Too few arguments');
        }

        if (type_of_type === 'string') {
          type = trim(type);

          if (type === '') {
            throw new Error('Bad type parameter');
          }

          for (j = 0; j < basic_types.length; j++) {
            basic_type = basic_types[j];

            if (basic_type == type) {
              for (i = 1; i < args_len; i++) {
                if (typeof args[i] !== type) {
                  throw new Error('Bad type');
                }
              }

              return;
            }
          }

          throw new Error('Bad type parameter');
        }

        // Not basic type. we need to use instanceof operator
        for (i = 1; i < args_len; i++) {
          if (!(args[i] instanceof type)) {
            throw new Error('Bad type');
          }
        }
      },
      /**
      * Checks if the specified array contains an element with specified value
      */
      has_value = function(array, value) {
        var i;
        verify_type(Array, array);

        for (i = 0; i < array.length; i++) {
          if (array[i] === value) {
            return true;
          }
        }

        return false;
      },
      /**
      * Checks the type of arguments
      * @param {String | Function} type Specifies the desired type
      * @return {Boolean} Return true if all arguments after the type argument are of specified type. Else false
      */
      are_type_of = function(type) {
        var args = arguments,
                args_len = arguments.length,
                basic_types = ['number', 'boolean', 'string', 'function', 'object', 'undefined'],
                basic_type, i, j, type_of_type = typeof type;
        if (type_of_type !== 'string' && type_of_type !== 'function') {
          throw new Error('Bad type parameter');
        }

        if (args_len < 2) {
          throw new Error('Too few arguments');
        }

        if (type_of_type === 'string') {
          type = trim(type);

          if (type === '') {
            return false;
          }

          for (j = 0; j < basic_types.length; j++) {
            basic_type = basic_types[j];

            if (basic_type == type) {
              for (i = 1; i < args_len; i++) {
                if (typeof args[i] != type) {
                  return false;
                }
              }

              return true;
            }
          }

          throw new Error('Bad type parameter');
        }

        // Not basic type. we need to use instanceof operator
        for (i = 1; i < args_len; i++) {
          if (!(args[i] instanceof type)) {
            return false;
          }
        }

        return true;
      },
      /*
      * Initialize and return an array with specified size and initial value
      */
      get_initialized_array = function(array_size, init_value) {
        var array = [],
                i;
        verify_type('number', array_size);

        for (i = 0; i < array_size; i++) {
          array.push(init_value);
        }

        return array;
      },
      /**
      * Splits text into lines and return as a string array
      */
      split_into_lines = function(text) {
        verify_type('string', text);

        if (text === '') {
          return [];
        }
        return text.split('\n');
      },
      is_empty_array = function(obj) {
        return are_type_of(Array, obj) && obj.length === 0;
      },
      /**
      * Finds longest common sequence between two sequences
      * @see {@link http://wordaligned.org/articles/longest-common-subsequence}
      */
      find_longest_common_sequence = function(seq1, seq2, seq1_is_in_lcs, seq2_is_in_lcs) {
        if (!are_type_of(Array, seq1, seq2)) {
          throw new Error('Array parameters are required');
        }

        // Deal with edge case
        if (is_empty_array(seq1) || is_empty_array(seq2)) {
          return [];
        }

        // Function to calculate lcs lengths
        var lcs_lens = function(xs, ys) {
          var i, j, prev,
            curr = get_initialized_array(ys.length + 1, 0);

          for (i = 0; i < xs.length; i++) {
            prev = curr.slice(0);
            for (j = 0; j < ys.length; j++) {
              if (xs[i] === ys[j]) {
                curr[j + 1] = prev[j] + 1;
              } else {
                curr[j + 1] = Math.max(curr[j], prev[j + 1]);
              }
            }
          }

          return curr;
        },
        // Function to find lcs and fill in the array to indicate the optimal longest common sequence
        find_lcs = function(xs, xidx, xs_is_in, ys) {
          var i, xb, xe, ll_b, ll_e, pivot, max, yb, ye,
            nx = xs.length,
            ny = ys.length;

          if (nx === 0) {
            return [];
          }
          if (nx === 1) {
            if (has_value(ys, xs[0])) {
              xs_is_in[xidx] = true;
              return [xs[0]];
            }
            return [];
          }
          i = Math.floor(nx / 2);
          xb = xs.slice(0, i);
          xe = xs.slice(i);
          ll_b = lcs_lens(xb, ys);
          ll_e = lcs_lens(xe.slice(0).reverse(), ys.slice(0).reverse());

          pivot = 0;
          max = 0;
          for (j = 0; j <= ny; j++) {
            if (ll_b[j] + ll_e[ny - j] > max) {
              pivot = j;
              max = ll_b[j] + ll_e[ny - j];
            }
          }
          yb = ys.slice(0, pivot);
          ye = ys.slice(pivot);
          return find_lcs(xb, xidx, xs_is_in, yb).concat(find_lcs(xe, xidx + i, xs_is_in, ye));
        };

        // Fill in seq1_is_in_lcs to find the optimal longest common subsequence of first sequence
        find_lcs(seq1, 0, seq1_is_in_lcs, seq2);
        // Fill in seq2_is_in_lcs to find the optimal longest common subsequence of second sequence and return the result
        return find_lcs(seq2, 0, seq2_is_in_lcs, seq1);
      };

  // First, check the parameters
  if (are_type_of('string', old_data, new_data) === false) {
    return false;
  }

  if (old_data == new_data) {
    return '';
  }

  if (typeof context_lines !== 'number' || context_lines > MAX_CONTEXT_LINES || context_lines < MIN_CONTEXT_LINES) {
    context_lines = DEFAULT_CONTEXT_LINES;
  }

  ori_lines = split_into_lines(old_data);
  new_lines = split_into_lines(new_data);
  var ori_len = ori_lines.length,
      new_len = new_lines.length,
      ori_is_in_lcs = get_initialized_array(ori_len, false),
      new_is_in_lcs = get_initialized_array(new_len, false),
      lcs_len = find_longest_common_sequence(ori_lines, new_lines, ori_is_in_lcs, new_is_in_lcs).length,
      unidiff = '';

  if (lcs_len === 0) { // No common sequence
    unidiff = HEADER_PREFIX + ORIGINAL_INDICATOR + (ori_len > 0 ? '1' : '0') + RANGE_SEPARATOR + ori_len + ' ' + NEW_INDICATOR + (new_len > 0 ? '1' : '0') + RANGE_SEPARATOR + new_len + HEADER_SUFFIX;

    for (i = 0; i < ori_len; i++) {
      unidiff += NEW_LINE + DELETION_INDICATOR + ori_lines[i];
    }

    for (j = 0; j < new_len; j++) {
      unidiff += NEW_LINE + ADDITION_INDICATOR + new_lines[j];
    }

    return unidiff;
  }

  var leading_context = [],
      trailing_context = [],
      actual_leading_context = [],
      actual_trailing_context = [],

      // Regularize leading context by the context_lines parameter
      regularize_leading_context = function(context) {
        if (context.length === 0 || context_lines === 0) {
          return [];
        }

        var context_start_pos = Math.max(context.length - context_lines, 0);

        return context.slice(context_start_pos);
      },

      // Regularize trailing context by the context_lines parameter
      regularize_trailing_context = function(context) {
        if (context.length === 0 || context_lines === 0) {
          return [];
        }

        return context.slice(0, Math.min(context_lines, context.length));
      };

  // Skip common lines in the beginning
  while (i < ori_len && ori_is_in_lcs[i] === true && new_is_in_lcs[i] === true) {
    leading_context.push(ori_lines[i]);
    i++;
  }

  j = i;
  k = i; // The index in the longest common sequence
  ori_hunk_start = i;
  new_hunk_start = j;
  ori_hunk_end = i;
  new_hunk_end = j;

  while (i < ori_len || j < new_len) {
    while (i < ori_len && ori_is_in_lcs[i] === false) {
      i++;
    }
    ori_hunk_end = i;

    while (j < new_len && new_is_in_lcs[j] === false) {
      j++;
    }
    new_hunk_end = j;

    // Find the trailing context
    trailing_context = [];
    while (i < ori_len && ori_is_in_lcs[i] === true && j < new_len && new_is_in_lcs[j] === true) {
      trailing_context.push(ori_lines[i]);
      k++;
      i++;
      j++;
    }

    if (k >= lcs_len || // No more in longest common lines
        trailing_context.length >= 2 * context_lines) { // Context break found
      if (trailing_context.length < 2 * context_lines) { // It must be last block of common lines but not a context break
        trailing_context = [];

        // Force break out
        i = ori_len;
        j = new_len;

        // Update hunk ends to force output to the end
        ori_hunk_end = ori_len;
        new_hunk_end = new_len;
      }

      // Output the diff hunk

      // Trim the leading and trailing context block
      actual_leading_context = regularize_leading_context(leading_context);
      actual_trailing_context = regularize_trailing_context(trailing_context);

      ori_hunk_start -= actual_leading_context.length;
      new_hunk_start -= actual_leading_context.length;
      ori_hunk_end += actual_trailing_context.length;
      new_hunk_end += actual_trailing_context.length;

      ori_hunk_line_no = ori_hunk_start + 1;
      new_hunk_line_no = new_hunk_start + 1;
      ori_hunk_size = ori_hunk_end - ori_hunk_start;
      new_hunk_size = new_hunk_end - new_hunk_start;

      // Build header
      unidiff += HEADER_PREFIX + ORIGINAL_INDICATOR + ori_hunk_line_no + RANGE_SEPARATOR + ori_hunk_size + ' ' + NEW_INDICATOR + new_hunk_line_no + RANGE_SEPARATOR + new_hunk_size + HEADER_SUFFIX + NEW_LINE;

      // Build the diff hunk content
      while (ori_hunk_start < ori_hunk_end || new_hunk_start < new_hunk_end) {
        if (ori_hunk_start < ori_hunk_end && ori_is_in_lcs[ori_hunk_start] === true && new_is_in_lcs[new_hunk_start] === true) { // The context line
          unidiff += CONTEXT_INDICATOR + ori_lines[ori_hunk_start] + NEW_LINE;
          ori_hunk_start++;
          new_hunk_start++;
        } else if (ori_hunk_start < ori_hunk_end && ori_is_in_lcs[ori_hunk_start] === false) { // The deletion line
          unidiff += DELETION_INDICATOR + ori_lines[ori_hunk_start] + NEW_LINE;
          ori_hunk_start++;
        } else if (new_hunk_start < new_hunk_end && new_is_in_lcs[new_hunk_start] === false) { // The additional line
          unidiff += ADDITION_INDICATOR + new_lines[new_hunk_start] + NEW_LINE;
          new_hunk_start++;
        }
      }

      // Update hunk position and leading context
      ori_hunk_start = i;
      new_hunk_start = j;
      leading_context = trailing_context;
    }
  }

  // Trim the trailing new line if it exists
  if (unidiff.length > 0 && unidiff.charAt(unidiff.length) === NEW_LINE) {
    unidiff = unidiff.slice(0, -1);
  }

  return unidiff;
}
<|endoftext|>


<commit_before>// 视图宽度
var pageWidth = $(window).width();
// 视图高度
var pageHeight = $(window).height();
// 裁剪图片高度
var contentHeight = $(".content").height();

// 头部尺寸对象
var headerObj = null;
// 车辆尺寸对象
var carObj = null;
// 底部尺寸对象
var footerObj = null;
// 二维码尺寸对象
var qrcodeObj = null;
// 裁剪图片对象
var imgEdit2;
// 生成图片路径
var carSrc = "";
// 上传标记
var uploadFlag = false;

// $(".page1").hide();

$("#produce").on("click",function(){
    // $('#carImg').css("visibility","visible");
    if(!uploadFlag){
      var img = new Image();
        img.src = "images/photo.png"
      img.onload = function () {
          imgEdit2 = new veImage({ canvas: document.getElementById('captureCanvas2'), image: this });
          $('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));
          carSrc = imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50);
          // $('#carImg').attr('src', "images/photo.png");
      };
    }
});

var data = ["images/canvas_header.png", "images/footer1.png"];
document.getElementById('uploadImage').onchange = function (e) {
  // var src, url = window.URL || window.webkitURL || window.mozURL, files = e.target.files;
  // for (var i = 0, len = files.length; i < len; ++i) {
  //   var file = files[i];
  //   if (url) {
  //     src = url.createObjectURL(file);
  //   } else {
  //     src = e.target.result;
  //   }
  // }
  var file = e.target.files[0];
  var Orientation = null;
  EXIF.getData(file, function () {
    EXIF.getAllTags(this);
    Orientation = EXIF.getTag(this, 'Orientation');
  });
  var src = '';
  var reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = function (e) {
    var image = new Image();
    image.src = e.target.result;
    image.onload = function () {
      var canvas = document.createElement("canvas");
      canvas.width = this.naturalWidth;
      canvas.height = this.naturalHeight;
      var ctx = canvas.getContext("2d");
      ctx.drawImage(this, 0, 0, this.naturalWidth, this.naturalHeight);
      var base64 = null;
      if (Orientation != "" && Orientation != 1 && Orientation != undefined) {
        var width = this.naturalWidth;
        var height = this.naturalHeight;
        switch (Orientation) {
          case 6://需要顺时针90度旋转
            canvas.width = height;
            canvas.height = width;
            ctx.rotate(90 * Math.PI / 180);
            ctx.drawImage(this, 0, -height);
            break;
          case 8://需要逆时针90度旋转
            canvas.width = height;
            canvas.height = width;
            ctx.rotate(-90 * Math.PI / 180);
            ctx.drawImage(this, -width, 0);
            break;
          case 3://需要180度旋转
            ctx.rotate(180 * Math.PI / 180);
            ctx.drawImage(this, -width, -height);
            break;
        }
      }
      src = canvas.toDataURL("image/png",0.8);
      $('.page7 .upload .uploadShow').attr("src",src).css("display","block");
      $("#photo").attr("src",src);
      $("#hecheng").attr("src",src);
      $('.page7 .upload .uploadBg').hide();
      $('.page7 .upload .uploadShow').attr("src",src).css("display","block");

      var img = new Image();
      img.src = src;
      img.onload = function () {
        imgEdit2 = new veImage({ canvas: document.getElementById('captureCanvas2'), image: this });
        $('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));
        carSrc = imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50);
        uploadFlag = true;
        // $('#carImg').attr('src', "images/photo.png");
      };
    };
  };

};

// 获取图片尺寸
function getSize(obj){
    var $obj = $("#"+obj);
    var w = $obj.width();
    var h = $obj.height();
    var x = $obj.offset().left;
    var y = $obj.offset().top;
    return {
        "w": w,
        "h": h,
        "x": x,
        "y": y
    }
}

function hecheng(){
	// 头部
  	headerObj = getSize("header");
  	// console.log(headerObj);
  	// 车辆
  	carObj = getSize("carImg");
  	// console.log(carObj);
  	// 底部
  	footerObj = getSize("footer");
  	// console.log(footerObj);
  	// 二维码
  	qrcodeObj = getSize("qrcode > img");
  	// console.log(qrcodeObj);

    data.unshift($('#carImg').attr('src'));
  	// data.unshift($('#qrcode > img').attr('src'));
	 data.push($('#qrcode > img').attr('src'));
	// console.log(data);
	draw(function(){
		// $("#photo").attr("src",base64[0]);
		$(".page9 > img").attr("src",base64[0]);
		$('.pag3').hide();
	})
}

var base64=[];

function draw(fn){
	var c=document.createElement('canvas'),
		ctx=c.getContext('2d'),
		len=data.length;
	c.width= $(window).width();
	c.height= $(window).height();
  c.style.width = $(window).width();
  c.style.height = $(window).height();
  // 屏幕的设备像素比
  var devicePixelRatio = window.devicePixelRatio || 1;

// 浏览器在渲染canvas之前存储画布信息的像素比
  var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
    ctx.mozBackingStorePixelRatio ||
    ctx.msBackingStorePixelRatio ||
    ctx.oBackingStorePixelRatio ||
    ctx.backingStorePixelRatio || 1;

// canvas的实际渲染倍率
  var ratio = devicePixelRatio / backingStoreRatio;

  c.width = c.width * ratio;
  c.height = c.height * ratio;
	// ctx.rect(0,0,c.width,c.height);
	// ctx.fillStyle='#fff';
	// ctx.fill();

	function drawing(n){
    var qrcode = $('.qrcode'),
      qrcodeW = 0,
      qrcodeH = 0;
		if(n<len){
			var img=new Image;
			// img.crossOrigin = 'Anonymous'; //解决跨域
      var va1 = $('.v1').val();
      var va2 = $('.v2').val();
      if (n==1) data[n] ='images/canvas_header' + (va2+"" +va1)+'.png';
			img.src=data[n];
			img.onload=function(){
				var x = 0;
				var y = 0;
				var w = "";
				var h = "";
				switch(n){
					case 0:
			            x = carObj.x;
			            y = carObj.y;
			            w = carObj.w;
			            h = carObj.h;
						break;
			        case 1:
			            x = headerObj.x;
			            y = headerObj.y;
			            w = headerObj.w;
			            h = headerObj.h;
			            break;
					case 2:
						x = footerObj.x;
						y = footerObj.y;
						w = footerObj.w;
						h = footerObj.h;
						break;
			        case 3:
			            x = 10;
			            y = pageHeight - 80;
			            w = 70;
			            h = 70;
			            break;
				}

				ctx.drawImage(img,x*ratio,y*ratio,w*ratio,h*ratio);
        drawing(n+1);//递归
			}
		}else{
			//保存生成作品图片
			base64.push(c.toDataURL("image/png", 1));
			fn();
		}
	}
	drawing(0);
}

// $("#btnCapture").on("click",function(){
// 	$('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));

//   	$('#carImg').css("display","block");
// });

$(".synthesis").on("click",function(){
	$(".page3").css("visibility","hidden");
  	$('.page9').show();

    if(uploadFlag){
      $('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));
    }else{
      if(carSrc){
        $('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));
      }else{
        $('#carImg').attr('src', "images/photo.png");
      }
    }
  	// $('#carImg').attr('src', "images/photo.png");
    // $('#carImg').css("display","block");
  	// $("#captureCanvas2").hide();
  	var img1 = new Image();
	  img1.src = $('#carImg').attr('src');
	  img1.onload = function () {
		  hecheng();
	  }
});

(function () {
  $(function () {
    $('.page7 .btn1').on('click', function () {
      document.querySelector('.upload-form').reset();
      $('.uploadShow').hide();
      $('.uploadBg').show();
    })
    var qrcode = new QRCode(document.querySelector(".qrcode"), {
      width : 90,
      height : 90
    });
    qrcode.makeCode('heep://www.baidu.com')
    var progress = 0;
    var queue = new createjs.LoadQueue(true);
    var queue2 = new createjs.LoadQueue(true);
    queue.loadManifest([
      'images/canvas_header.png',
      'images/footer.png',
      'images/music.mp3',
      'images/adjust.png',
      'images/arrow.png',
      'images/bg.jpg',
      'images/bg2.jpg',
      'images/bg3.jpg',
      'images/bg4.jpg',
      'images/bg5.jpg',
      'images/btn1.png',
      'images/btn2.png',
      'images/btn3.png',
      'images/btn4.png',
      'images/btn5.png',
      'images/btn6.png',
      'images/btn7.png',
      'images/btn8.png',
      'images/btn9.png',
      'images/btn10.png',
      'images/btn22.png',
      'images/car.png',
      'images/car2.png',
      'images/close.png',
      'images/fabulous.png',
      'images/form.png',
      'images/frame.png',
      'images/light.png',
      'images/logo.png',
      'images/man.png',
      'images/photo.png',
      'images/poster.png',
      'images/qrcode.png',
      'images/rank.png',
      // 'images/rule-bg.png',
      'images/shadow.png',
      'images/success.png',
      'images/tag1.png',
      'images/tag2.png',
      'images/title1-bg.png',
      'images/title2-bg.png',
      'images/title3-bg.png',
      'images/title4-bg.png',
      'images/upload.png',
      'images/woman.png',
      'images/word.png',
      'images/word2.png',
      'images/word3.png',
      'images/word4.png',
    ]);
    queue2.loadManifest([
      'images/canvas_header11.png',
      'images/canvas_header12.png',
      'images/canvas_header13.png',
      'images/canvas_header21.png',
      'images/canvas_header22.png',
      'images/canvas_header23.png',
      'images/canvas_header31.png',
      'images/canvas_header32.png',
      'images/canvas_header33.png',
      'images/canvas_header41.png',
      'images/canvas_header42.png',
      'images/canvas_header43.png',
      'images/canvas_header51.png',
      'images/canvas_header52.png',
      'images/canvas_header53.png'
    ]);
    queue.on("complete", function (e) {
      $('.page-load').hide();
      queue2.on("complete", function (e) {
       console.log(789)
      }, this);
    }, this);
    queue.on("fileload", function (e) {
      progress ++;
      $('.page-load i').width(parseInt(progress/47*100)+"%");
      $('.page-load em').html(parseInt(progress/47*100)+"%");
      // console.log(parseInt(progress/44*100)+"%")
    }, this);
    function audioAutoPlay(id){
      var audio = document.getElementById(id);
      audio.play();
      document.addEventListener("WeixinJSBridgeReady", function () {
        audio.play();
      }, false);
    }
    audioAutoPlay('audio_btn');
    $('.video_exist').on('click', function () {
      var media = document.getElementById('media');
      if (media.paused) {
        media.play();
        $(this).addClass('rotate');
      }else {
        media.pause();
        $(this).removeClass('rotate');
      }
    });

    //联动
    var urlChina = 'js/data.js';
    $.cxSelect.defaults.url = urlChina;
    // 默认
    $('#city_china').cxSelect({
      selects: ['province', 'city', 'area']
    });
    $('.page1 .btn1').on('click', function () {
      $('.page7').show();
      $('.page1').hide();
    });
    $('.page1 .btn2').on('click', function () {
      $('.page2').show();
      $('.page1').hide();
    });
    $('.page1 .btn3').on('click', function () {
      $('.page5').show();
      $('.page1').hide();
    });
    $('.page2 .close').on('click', function () {
      $('.page2').hide();
      $('.page1').show();
    });
    $('.page5 .ƒorm').on('submit', function (e) {
      e.preventDefault();
      $('.page5').hide();
      $('.page8').show();
    });
    $('.page4 .btn2').on('click', function () {
      $('.page4').hide();
      $('.page6').show();
    });
    $('.page7 .btn2').on('click', function () {
      var file = $('#uploadImage').val();
      if (!file) return alert('请选择图哦~');
      $('.page7').hide();
      // $('.page3').show();
      $(".page3").css("visibility","visible");
      var va1 = $('.v1').val();
      var va2 = $('.v2').val();
      $('.page3 .header > img').attr('src','./images/canvas_header'+(va2+"" +va1)+'.png');
    });
    $('.page8 .btn9').on('click', function () {
      $('.page8').hide();
      $('.page1').show();
    });
   $('.page6 .close').on('click', function () {
     $('.page6').hide();
     $('.page7').show();
   });
   $('.gender .g').on('click', function () {
     $(this).addClass('active').siblings().removeClass('active')
   });
  })
})()<commit_msg>ios自动播放音乐<commit_after>// 视图宽度
var pageWidth = $(window).width();
// 视图高度
var pageHeight = $(window).height();
// 裁剪图片高度
var contentHeight = $(".content").height();

// 头部尺寸对象
var headerObj = null;
// 车辆尺寸对象
var carObj = null;
// 底部尺寸对象
var footerObj = null;
// 二维码尺寸对象
var qrcodeObj = null;
// 裁剪图片对象
var imgEdit2;
// 生成图片路径
var carSrc = "";
// 上传标记
var uploadFlag = false;

// $(".page1").hide();

$("#produce").on("click",function(){
    // $('#carImg').css("visibility","visible");
    if(!uploadFlag){
      var img = new Image();
        img.src = "images/photo.png"
      img.onload = function () {
          imgEdit2 = new veImage({ canvas: document.getElementById('captureCanvas2'), image: this });
          $('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));
          carSrc = imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50);
          // $('#carImg').attr('src', "images/photo.png");
      };
    }
});

var data = ["images/canvas_header.png", "images/footer1.png"];
document.getElementById('uploadImage').onchange = function (e) {
  // var src, url = window.URL || window.webkitURL || window.mozURL, files = e.target.files;
  // for (var i = 0, len = files.length; i < len; ++i) {
  //   var file = files[i];
  //   if (url) {
  //     src = url.createObjectURL(file);
  //   } else {
  //     src = e.target.result;
  //   }
  // }
  var file = e.target.files[0];
  var Orientation = null;
  EXIF.getData(file, function () {
    EXIF.getAllTags(this);
    Orientation = EXIF.getTag(this, 'Orientation');
  });
  var src = '';
  var reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = function (e) {
    var image = new Image();
    image.src = e.target.result;
    image.onload = function () {
      var canvas = document.createElement("canvas");
      canvas.width = this.naturalWidth;
      canvas.height = this.naturalHeight;
      var ctx = canvas.getContext("2d");
      ctx.drawImage(this, 0, 0, this.naturalWidth, this.naturalHeight);
      var base64 = null;
      if (Orientation != "" && Orientation != 1 && Orientation != undefined) {
        var width = this.naturalWidth;
        var height = this.naturalHeight;
        switch (Orientation) {
          case 6://需要顺时针90度旋转
            canvas.width = height;
            canvas.height = width;
            ctx.rotate(90 * Math.PI / 180);
            ctx.drawImage(this, 0, -height);
            break;
          case 8://需要逆时针90度旋转
            canvas.width = height;
            canvas.height = width;
            ctx.rotate(-90 * Math.PI / 180);
            ctx.drawImage(this, -width, 0);
            break;
          case 3://需要180度旋转
            ctx.rotate(180 * Math.PI / 180);
            ctx.drawImage(this, -width, -height);
            break;
        }
      }
      src = canvas.toDataURL("image/png",0.8);
      $('.page7 .upload .uploadShow').attr("src",src).css("display","block");
      $("#photo").attr("src",src);
      $("#hecheng").attr("src",src);
      $('.page7 .upload .uploadBg').hide();
      $('.page7 .upload .uploadShow').attr("src",src).css("display","block");

      var img = new Image();
      img.src = src;
      img.onload = function () {
        imgEdit2 = new veImage({ canvas: document.getElementById('captureCanvas2'), image: this });
        $('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));
        carSrc = imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50);
        uploadFlag = true;
        // $('#carImg').attr('src', "images/photo.png");
      };
    };
  };

};

// 获取图片尺寸
function getSize(obj){
    var $obj = $("#"+obj);
    var w = $obj.width();
    var h = $obj.height();
    var x = $obj.offset().left;
    var y = $obj.offset().top;
    return {
        "w": w,
        "h": h,
        "x": x,
        "y": y
    }
}

function hecheng(){
	// 头部
  	headerObj = getSize("header");
  	// console.log(headerObj);
  	// 车辆
  	carObj = getSize("carImg");
  	// console.log(carObj);
  	// 底部
  	footerObj = getSize("footer");
  	// console.log(footerObj);
  	// 二维码
  	qrcodeObj = getSize("qrcode > img");
  	// console.log(qrcodeObj);

    data.unshift($('#carImg').attr('src'));
  	// data.unshift($('#qrcode > img').attr('src'));
	 data.push($('#qrcode > img').attr('src'));
	// console.log(data);
	draw(function(){
		// $("#photo").attr("src",base64[0]);
		$(".page9 > img").attr("src",base64[0]);
		$('.pag3').hide();
	})
}

var base64=[];

function draw(fn){
	var c=document.createElement('canvas'),
		ctx=c.getContext('2d'),
		len=data.length;
	c.width= $(window).width();
	c.height= $(window).height();
  c.style.width = $(window).width();
  c.style.height = $(window).height();
  // 屏幕的设备像素比
  var devicePixelRatio = window.devicePixelRatio || 1;

// 浏览器在渲染canvas之前存储画布信息的像素比
  var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
    ctx.mozBackingStorePixelRatio ||
    ctx.msBackingStorePixelRatio ||
    ctx.oBackingStorePixelRatio ||
    ctx.backingStorePixelRatio || 1;

// canvas的实际渲染倍率
  var ratio = devicePixelRatio / backingStoreRatio;

  c.width = c.width * ratio;
  c.height = c.height * ratio;
	// ctx.rect(0,0,c.width,c.height);
	// ctx.fillStyle='#fff';
	// ctx.fill();

	function drawing(n){
    var qrcode = $('.qrcode'),
      qrcodeW = 0,
      qrcodeH = 0;
		if(n<len){
			var img=new Image;
			// img.crossOrigin = 'Anonymous'; //解决跨域
      var va1 = $('.v1').val();
      var va2 = $('.v2').val();
      if (n==1) data[n] ='images/canvas_header' + (va2+"" +va1)+'.png';
			img.src=data[n];
			img.onload=function(){
				var x = 0;
				var y = 0;
				var w = "";
				var h = "";
				switch(n){
					case 0:
			            x = carObj.x;
			            y = carObj.y;
			            w = carObj.w;
			            h = carObj.h;
						break;
			        case 1:
			            x = headerObj.x;
			            y = headerObj.y;
			            w = headerObj.w;
			            h = headerObj.h;
			            break;
					case 2:
						x = footerObj.x;
						y = footerObj.y;
						w = footerObj.w;
						h = footerObj.h;
						break;
			        case 3:
			            x = 10;
			            y = pageHeight - 80;
			            w = 70;
			            h = 70;
			            break;
				}

				ctx.drawImage(img,x*ratio,y*ratio,w*ratio,h*ratio);
        drawing(n+1);//递归
			}
		}else{
			//保存生成作品图片
			base64.push(c.toDataURL("image/png", 1));
			fn();
		}
	}
	drawing(0);
}

// $("#btnCapture").on("click",function(){
// 	$('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));

//   	$('#carImg').css("display","block");
// });

$(".synthesis").on("click",function(){
	$(".page3").css("visibility","hidden");
  	$('.page9').show();

    if(uploadFlag){
      $('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));
    }else{
      if(carSrc){
        $('#carImg').attr('src', imgEdit2.crop(2*pageWidth, 2*contentHeight, 0, 50));
      }else{
        $('#carImg').attr('src', "images/photo.png");
      }
    }
  	// $('#carImg').attr('src', "images/photo.png");
    // $('#carImg').css("display","block");
  	// $("#captureCanvas2").hide();
  	var img1 = new Image();
	  img1.src = $('#carImg').attr('src');
	  img1.onload = function () {
		  hecheng();
	  }
});

(function () {
  $(function () {
    $('.page7 .btn1').on('click', function () {
      document.querySelector('.upload-form').reset();
      $('.uploadShow').hide();
      $('.uploadBg').show();
    })
    var qrcode = new QRCode(document.querySelector(".qrcode"), {
      width : 90,
      height : 90
    });
    qrcode.makeCode('heep://www.baidu.com')
    var progress = 0;
    var queue = new createjs.LoadQueue(true);
    var queue2 = new createjs.LoadQueue(true);
    queue.loadManifest([
      'images/canvas_header.png',
      'images/footer.png',
      'images/music.mp3',
      'images/adjust.png',
      'images/arrow.png',
      'images/bg.jpg',
      'images/bg2.jpg',
      'images/bg3.jpg',
      'images/bg4.jpg',
      'images/bg5.jpg',
      'images/btn1.png',
      'images/btn2.png',
      'images/btn3.png',
      'images/btn4.png',
      'images/btn5.png',
      'images/btn6.png',
      'images/btn7.png',
      'images/btn8.png',
      'images/btn9.png',
      'images/btn10.png',
      'images/btn22.png',
      'images/car.png',
      'images/car2.png',
      'images/close.png',
      'images/fabulous.png',
      'images/form.png',
      'images/frame.png',
      'images/light.png',
      'images/logo.png',
      'images/man.png',
      'images/photo.png',
      'images/poster.png',
      'images/qrcode.png',
      'images/rank.png',
      // 'images/rule-bg.png',
      'images/shadow.png',
      'images/success.png',
      'images/tag1.png',
      'images/tag2.png',
      'images/title1-bg.png',
      'images/title2-bg.png',
      'images/title3-bg.png',
      'images/title4-bg.png',
      'images/upload.png',
      'images/woman.png',
      'images/word.png',
      'images/word2.png',
      'images/word3.png',
      'images/word4.png',
    ]);
    queue2.loadManifest([
      'images/canvas_header11.png',
      'images/canvas_header12.png',
      'images/canvas_header13.png',
      'images/canvas_header21.png',
      'images/canvas_header22.png',
      'images/canvas_header23.png',
      'images/canvas_header31.png',
      'images/canvas_header32.png',
      'images/canvas_header33.png',
      'images/canvas_header41.png',
      'images/canvas_header42.png',
      'images/canvas_header43.png',
      'images/canvas_header51.png',
      'images/canvas_header52.png',
      'images/canvas_header53.png'
    ]);
    queue.on("complete", function (e) {
      $('.page-load').hide();
      queue2.on("complete", function (e) {
       console.log(789)
      }, this);
    }, this);
    queue.on("fileload", function (e) {
      progress ++;
      $('.page-load i').width(parseInt(progress/47*100)+"%");
      $('.page-load em').html(parseInt(progress/47*100)+"%");
      // console.log(parseInt(progress/44*100)+"%")
    }, this);
    function audioAutoPlay(id){
      var audio = document.getElementById(id);
      audio.play();
      document.addEventListener("WeixinJSBridgeReady", function () {
        audio.play();
      }, false);
    }
    audioAutoPlay('media');
    $('.video_exist').on('click', function () {
      var media = document.getElementById('media');
      if (media.paused) {
        media.play();
        $(this).addClass('rotate');
      }else {
        media.pause();
        $(this).removeClass('rotate');
      }
    });

    //联动
    var urlChina = 'js/data.js';
    $.cxSelect.defaults.url = urlChina;
    // 默认
    $('#city_china').cxSelect({
      selects: ['province', 'city', 'area']
    });
    $('.page1 .btn1').on('click', function () {
      $('.page7').show();
      $('.page1').hide();
    });
    $('.page1 .btn2').on('click', function () {
      $('.page2').show();
      $('.page1').hide();
    });
    $('.page1 .btn3').on('click', function () {
      $('.page5').show();
      $('.page1').hide();
    });
    $('.page2 .close').on('click', function () {
      $('.page2').hide();
      $('.page1').show();
    });
    $('.page5 .ƒorm').on('submit', function (e) {
      e.preventDefault();
      $('.page5').hide();
      $('.page8').show();
    });
    $('.page4 .btn2').on('click', function () {
      $('.page4').hide();
      $('.page6').show();
    });
    $('.page7 .btn2').on('click', function () {
      var file = $('#uploadImage').val();
      if (!file) return alert('请选择图哦~');
      $('.page7').hide();
      // $('.page3').show();
      $(".page3").css("visibility","visible");
      var va1 = $('.v1').val();
      var va2 = $('.v2').val();
      $('.page3 .header > img').attr('src','./images/canvas_header'+(va2+"" +va1)+'.png');
    });
    $('.page8 .btn9').on('click', function () {
      $('.page8').hide();
      $('.page1').show();
    });
   $('.page6 .close').on('click', function () {
     $('.page6').hide();
     $('.page7').show();
   });
   $('.gender .g').on('click', function () {
     $(this).addClass('active').siblings().removeClass('active')
   });
  })
})()<|endoftext|>


<commit_before>// @flow

import React from 'react'
import {
  Button,
  Col,
  Checkbox,
  ControlLabel,
  Form,
  FormControl,
  FormGroup,
  HelpBlock,
  ListGroup,
  ListGroupItem,
  Panel,
  Row
} from 'react-bootstrap'
import update from 'react-addons-update'
import { connect } from 'react-redux'

import { createLabel, updateLabel } from '../../manager/actions/labels'
import { validationState } from '../util'
import type { ManagerUserState } from '../../types/reducers'
import type { Label } from '../../types'

type Props = {
  createLabel: Function,
  label: Label,
  onDone?: Function,
  projectId: string,
  updateLabel: Function,
  user: ManagerUserState
}

type State = {
  labelIsNew?: boolean,
  newLabel: Label,
  validation: {
    description: boolean,
    name: boolean
  }
}

/**
 * Component to edit a label. If a label is specified, that label will be pre-filled. If a new label
 * is to be created, a blank object and project id must be passed in.
 */
class LabelEditor extends React.Component<Props, State> {
    state = {
      validation: {
        name: true,
        description: true
      },
      newLabel: {...this.props.label},
      labelIsNew: false
    }

    componentDidMount = () => {
      // If we didn't get a label passed in, declare the label to be new
      if (Object.keys(this.props.label).length === 0) {
        this.setState({
          labelIsNew: true
        })
      }
    }

    userIsAdmin = (user: ManagerUserState) => {
      const {permissions} = user
      if (!permissions) return false
      else return permissions.isApplicationAdmin() || permissions.canAdministerAnOrganization()
    }

  _onFormChange = ({target}: {target: HTMLInputElement}, alwaysValid: boolean) => {
    const {name, value, type, checked} = target

    const universalValue: string | boolean = type === 'checkbox' ? checked : value
    // extra check for flow
    const valid =
      alwaysValid ||
      (typeof universalValue === 'string' && universalValue.length > 0)

    this.setState(
      update(this.state, {
        newLabel: { $merge: {[name]: universalValue} },
        validation: { [name]: { $set: universalValue && valid } }
      })
    )
  }

  /* Compares the dirty label from the old one to determine
  which keys changed
  */
  _getChanges = () => {
    const newLabel = this.state.newLabel
    const oldLabel = this.props.label

    const changes: any = {}
    Object.keys(newLabel).forEach(k => {
      if (newLabel[k] !== oldLabel[k]) {
        changes[k] = newLabel[k]
      }
    })
    return changes
  }

  _settingsAreUnedited = () => Object.keys(this._getChanges()).length === 0

  _formIsValid = () => {
    const {validation} = this.state
    return Object.keys(validation).every(k => validation[k])
  }

  _onSaveSettings = () => {
    const {labelIsNew, newLabel: label} = this.state
    const {createLabel, updateLabel} = this.props

    if (labelIsNew) {
      // Insert project ID
      label.projectId = this.props.projectId
      createLabel(label)
    } else {
      updateLabel(label)
    }

    // This passed method will close the editor
    this.props.onDone && this.props.onDone()
  }

  render () {
    const { newLabel, validation } = this.state
    const { user } = this.props

    return (
      <Form>
        <Panel>
          <ListGroup fill>
            <ListGroupItem>
              <FormGroup validationState={validationState(validation.name)}>
                <ControlLabel>Name</ControlLabel>
                <FormControl
                  value={newLabel.name}
                  name={'name'}
                  onChange={this._onFormChange}
                />
                <FormControl.Feedback />
                <HelpBlock>Required.</HelpBlock>
              </FormGroup>
              <FormGroup>
                <ControlLabel>Description</ControlLabel>
                <FormControl
                  value={newLabel.description}
                  name={'description'}
                  onChange={(e) => this._onFormChange(e, true)}
                />
                <FormControl.Feedback />
              </FormGroup>
            </ListGroupItem>
            <ListGroupItem>
              <Row>
                <Col xs={3}>
                  <FormGroup>
                    <ControlLabel>Color</ControlLabel>
                    <FormControl
                      value={newLabel.color}
                      name={'color'}
                      type='color'
                      onChange={this._onFormChange}
                    />
                    <FormControl.Feedback />
                  </FormGroup>
                </Col>
                {this.userIsAdmin(user) && (
                  <Col xs={9}>
                    <Checkbox
                      checked={newLabel.adminOnly}
                      name={'adminOnly'}
                      type='checkbox'
                      onChange={(e) => this._onFormChange(e, true)}
                      style={{margin: 0}}
                    >
                      Visible to admins only?
                    </Checkbox>

                                          If checked, this label will only be visible to
                                          project and application admins. Users with
                                          feed-specific priveleges will not see this label
                                          applied to any feed sources.

                  </Col>
                )}
              </Row>
            </ListGroupItem>
          </ListGroup>
        </Panel>
        <Row>
          <Col xs={12}>
            {/* Cancel button */}
            <Button onClick={this.props.onDone} style={{ marginRight: 10 }}>
              Cancel
            </Button>
            {/* Save button */}
            <Button
              bsStyle='primary'
              disabled={this._settingsAreUnedited() || !this._formIsValid()}
              type='submit'
              onClick={this._onSaveSettings}
            >
              Save
            </Button>
          </Col>
        </Row>
      </Form>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    newLabel: state.newLabel,
    user: state.user
  }
}

const mapDispatchToProps = {
  createLabel, updateLabel
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(LabelEditor)
<commit_msg>refactor(LabelEditor): cleaner validation for checkbox<commit_after>// @flow

import React from 'react'
import {
  Button,
  Col,
  Checkbox,
  ControlLabel,
  Form,
  FormControl,
  FormGroup,
  HelpBlock,
  ListGroup,
  ListGroupItem,
  Panel,
  Row
} from 'react-bootstrap'
import update from 'react-addons-update'
import { connect } from 'react-redux'

import { createLabel, updateLabel } from '../../manager/actions/labels'
import { validationState } from '../util'
import type { ManagerUserState } from '../../types/reducers'
import type { Label } from '../../types'

type Props = {
  createLabel: Function,
  label: Label,
  onDone?: Function,
  projectId: string,
  updateLabel: Function,
  user: ManagerUserState
}

type State = {
  labelIsNew?: boolean,
  newLabel: Label,
  validation: {
    description: boolean,
    name: boolean
  }
}

/**
 * Component to edit a label. If a label is specified, that label will be pre-filled. If a new label
 * is to be created, a blank object and project id must be passed in.
 */
class LabelEditor extends React.Component<Props, State> {
    state = {
      validation: {
        name: true,
        description: true
      },
      newLabel: {...this.props.label},
      labelIsNew: false
    }

    componentDidMount = () => {
      // If we didn't get a label passed in, declare the label to be new
      if (Object.keys(this.props.label).length === 0) {
        this.setState({
          labelIsNew: true
        })
      }
    }

    userIsAdmin = (user: ManagerUserState) => {
      const {permissions} = user
      if (!permissions) return false
      else return permissions.isApplicationAdmin() || permissions.canAdministerAnOrganization()
    }

  _onFormChange = ({target}: {target: HTMLInputElement}) => {
    const {name, value} = target

    this.setState(
      update(this.state, {
        newLabel: { $merge: {[name]: value} },
        validation: { [name]: { $set: value && value.length > 0 } }
      })
    )
  }

  _onCheckboxChange = ({target}: {target: HTMLInputElement}) => {
    const {name, checked} = target

    this.setState(
      update(this.state, {
        newLabel: { $merge: {[name]: checked} },
        // Checkboxes are valid in any state
        validation: { [name]: { $set: true } }
      })
    )
  }

  /* Compares the dirty label from the old one to determine
  which keys changed
  */
  _getChanges = () => {
    const newLabel = this.state.newLabel
    const oldLabel = this.props.label

    const changes: any = {}
    Object.keys(newLabel).forEach(k => {
      if (newLabel[k] !== oldLabel[k]) {
        changes[k] = newLabel[k]
      }
    })
    return changes
  }

  _settingsAreUnedited = () => Object.keys(this._getChanges()).length === 0

  _formIsValid = () => {
    const {validation} = this.state
    return Object.keys(validation).every(k => validation[k])
  }

  _onSaveSettings = () => {
    const {labelIsNew, newLabel: label} = this.state
    const {createLabel, updateLabel} = this.props

    if (labelIsNew) {
      // Insert project ID
      label.projectId = this.props.projectId
      createLabel(label)
    } else {
      updateLabel(label)
    }

    // This passed method will close the editor
    this.props.onDone && this.props.onDone()
  }

  render () {
    const { newLabel, validation } = this.state
    const { user } = this.props

    return (
      <Form>
        <Panel>
          <ListGroup fill>
            <ListGroupItem>
              <FormGroup validationState={validationState(validation.name)}>
                <ControlLabel>Name</ControlLabel>
                <FormControl
                  value={newLabel.name}
                  name={'name'}
                  onChange={this._onFormChange}
                />
                <FormControl.Feedback />
                <HelpBlock>Required.</HelpBlock>
              </FormGroup>
              <FormGroup>
                <ControlLabel>Description</ControlLabel>
                <FormControl
                  value={newLabel.description}
                  name={'description'}
                  onChange={(e) => this._onFormChange(e, true)}
                />
                <FormControl.Feedback />
              </FormGroup>
            </ListGroupItem>
            <ListGroupItem>
              <Row>
                <Col xs={3}>
                  <FormGroup>
                    <ControlLabel>Color</ControlLabel>
                    <FormControl
                      value={newLabel.color}
                      name={'color'}
                      type='color'
                      onChange={this._onFormChange}
                    />
                    <FormControl.Feedback />
                  </FormGroup>
                </Col>
                {this.userIsAdmin(user) && (
                  <Col xs={9}>
                    <Checkbox
                      checked={newLabel.adminOnly}
                      name='adminOnly'
                      type='checkbox'
                      onChange={(e) => this._onCheckboxChange(e)}
                      style={{margin: 0}}
                    >
                      Visible to admins only?
                    </Checkbox>

                                          If checked, this label will only be visible to
                                          project and application admins. Users with
                                          feed-specific priveleges will not see this label
                                          applied to any feed sources.

                  </Col>
                )}
              </Row>
            </ListGroupItem>
          </ListGroup>
        </Panel>
        <Row>
          <Col xs={12}>
            {/* Cancel button */}
            <Button onClick={this.props.onDone} style={{ marginRight: 10 }}>
              Cancel
            </Button>
            {/* Save button */}
            <Button
              bsStyle='primary'
              disabled={this._settingsAreUnedited() || !this._formIsValid()}
              type='submit'
              onClick={this._onSaveSettings}
            >
              Save
            </Button>
          </Col>
        </Row>
      </Form>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    newLabel: state.newLabel,
    user: state.user
  }
}

const mapDispatchToProps = {
  createLabel, updateLabel
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(LabelEditor)
<|endoftext|>