<commit_before>/* @flow */

import React from 'react'
import PropTypes from 'prop-types'
import ReactNative, {
  Keyboard,
  Platform,
  UIManager,
  TextInput,
  findNodeHandle,
  Animated
} from 'react-native'
import { isIphoneX } from 'react-native-iphone-x-helper'
import type { KeyboardAwareInterface } from './KeyboardAwareInterface'

const _KAM_DEFAULT_TAB_BAR_HEIGHT: number = isIphoneX() ? 83 : 49
const _KAM_KEYBOARD_OPENING_TIME: number = 250
const _KAM_EXTRA_HEIGHT: number = 75

const supportedKeyboardEvents = [
  'keyboardWillShow',
  'keyboardDidShow',
  'keyboardWillHide',
  'keyboardDidHide',
  'keyboardWillChangeFrame',
  'keyboardDidChangeFrame'
]
const keyboardEventToCallbackName = (eventName: string) =>
  'on' + eventName[0].toUpperCase() + eventName.substring(1)
const keyboardEventPropTypes = supportedKeyboardEvents.reduce(
  (acc: Object, eventName: string) => ({
    ...acc,
    [keyboardEventToCallbackName(eventName)]: PropTypes.func
  }),
  {}
)
const keyboardAwareHOCTypeEvents = supportedKeyboardEvents.reduce(
  (acc: Object, eventName: string) => ({
    ...acc,
    [keyboardEventToCallbackName(eventName)]: Function
  }),
  {}
)

export type KeyboardAwareHOCProps = {
  viewIsInsideTabBar?: boolean,
  resetScrollToCoords?: {
    x: number,
    y: number
  },
  enableResetScrollToCoords?: boolean,
  enableAutomaticScroll?: boolean,
  extraHeight?: number,
  extraScrollHeight?: number,
  keyboardOpeningTime?: number,
  onScroll?: Function,
  update?: Function,
  contentContainerStyle?: any,
  enableOnAndroid?: boolean,
  innerRef?: Function,
  ...keyboardAwareHOCTypeEvents
}
export type KeyboardAwareHOCState = {
  keyboardSpace: number
}

export type ElementLayout = {
  x: number,
  y: number,
  width: number,
  height: number
}

export type ContentOffset = {
  x: number,
  y: number
}

export type ScrollPosition = {
  x: number,
  y: number,
  animated: boolean
}

export type ScrollIntoViewOptions = ?{
  getScrollPosition?: (
    parentLayout: ElementLayout,
    childLayout: ElementLayout,
    contentOffset: ContentOffset
  ) => ScrollPosition
}

export type KeyboardAwareHOCOptions = ?{
  enableOnAndroid: boolean,
  contentContainerStyle: ?Object,
  enableAutomaticScroll: boolean,
  extraHeight: number,
  extraScrollHeight: number,
  enableResetScrollToCoords: boolean,
  keyboardOpeningTime: number,
  viewIsInsideTabBar: boolean,
  refPropName: string,
  extractNativeRef: Function
}

function getDisplayName(WrappedComponent: React$Component) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

const ScrollIntoViewDefaultOptions: KeyboardAwareHOCOptions = {
  enableOnAndroid: false,
  contentContainerStyle: undefined,
  enableAutomaticScroll: true,
  extraHeight: _KAM_EXTRA_HEIGHT,
  extraScrollHeight: 0,
  enableResetScrollToCoords: true,
  keyboardOpeningTime: _KAM_KEYBOARD_OPENING_TIME,
  viewIsInsideTabBar: false,

  // The ref prop name that will be passed to the wrapped component to obtain a ref
  // If your ScrollView is already wrapped, maybe the wrapper permit to get a ref
  // For example, with glamorous-native ScrollView, you should use "innerRef"
  refPropName: 'ref',
  // Sometimes the ref you get is a ref to a wrapped view (ex: Animated.ScrollView)
  // We need access to the imperative API of a real native ScrollView so we need extraction logic
  extractNativeRef: (ref: Object) => {
    // getNode() permit to support Animated.ScrollView automatically
    // see https://github.com/facebook/react-native/issues/19650
    // see https://stackoverflow.com/questions/42051368/scrollto-is-undefined-on-animated-scrollview/48786374
    if (ref.getNode) {
      return ref.getNode()
    } else {
      return ref
    }
  }
}

function KeyboardAwareHOC(
  ScrollableComponent: React$Component,
  userOptions: KeyboardAwareHOCOptions
) {
  const hocOptions: KeyboardAwareHOCOptions = {
    ...ScrollIntoViewDefaultOptions,
    ...userOptions
  }

  return class
    extends React.Component<KeyboardAwareHOCProps, KeyboardAwareHOCState>
    implements KeyboardAwareInterface {
    _rnkasv_keyboardView: any
    keyboardWillShowEvent: ?Function
    keyboardWillHideEvent: ?Function
    position: ContentOffset
    defaultResetScrollToCoords: ?{ x: number, y: number }
    resetCoords: ?{ x: number, y: number }
    mountedComponent: boolean
    handleOnScroll: Function
    state: KeyboardAwareHOCState
    static displayName = `KeyboardAware${getDisplayName(ScrollableComponent)}`

    static propTypes = {
      viewIsInsideTabBar: PropTypes.bool,
      resetScrollToCoords: PropTypes.shape({
        x: PropTypes.number.isRequired,
        y: PropTypes.number.isRequired
      }),
      enableResetScrollToCoords: PropTypes.bool,
      enableAutomaticScroll: PropTypes.bool,
      extraHeight: PropTypes.number,
      extraScrollHeight: PropTypes.number,
      keyboardOpeningTime: PropTypes.number,
      onScroll: PropTypes.oneOfType([
        PropTypes.func, // Normal listener
        PropTypes.object // Animated.event listener
      ]),
      update: PropTypes.func,
      contentContainerStyle: PropTypes.any,
      enableOnAndroid: PropTypes.bool,
      innerRef: PropTypes.func,
      ...keyboardEventPropTypes
    }

    // HOC options are used to init default props, so that these options can be overriden with component props
    static defaultProps = {
      enableAutomaticScroll: hocOptions.enableAutomaticScroll,
      extraHeight: hocOptions.extraHeight,
      extraScrollHeight: hocOptions.extraScrollHeight,
      enableResetScrollToCoords: hocOptions.enableResetScrollToCoords,
      keyboardOpeningTime: hocOptions.keyboardOpeningTime,
      viewIsInsideTabBar: hocOptions.viewIsInsideTabBar,
      enableOnAndroid: hocOptions.enableOnAndroid
    }

    constructor(props: KeyboardAwareHOCProps) {
      super(props)
      this.keyboardWillShowEvent = undefined
      this.keyboardWillHideEvent = undefined
      this.callbacks = {}
      this.position = { x: 0, y: 0 }
      this.defaultResetScrollToCoords = null
      const keyboardSpace: number = props.viewIsInsideTabBar
        ? _KAM_DEFAULT_TAB_BAR_HEIGHT
        : 0
      this.state = { keyboardSpace }
    }

    componentDidMount() {
      this.mountedComponent = true
      // Keyboard events
      if (Platform.OS === 'ios') {
        this.keyboardWillShowEvent = Keyboard.addListener(
          'keyboardWillShow',
          this._updateKeyboardSpace
        )
        this.keyboardWillHideEvent = Keyboard.addListener(
          'keyboardWillHide',
          this._resetKeyboardSpace
        )
      } else if (Platform.OS === 'android' && this.props.enableOnAndroid) {
        this.keyboardWillShowEvent = Keyboard.addListener(
          'keyboardDidShow',
          this._updateKeyboardSpace
        )
        this.keyboardWillHideEvent = Keyboard.addListener(
          'keyboardDidHide',
          this._resetKeyboardSpace
        )
      }

      supportedKeyboardEvents.forEach((eventName: string) => {
        const callbackName = keyboardEventToCallbackName(eventName)
        if (this.props[callbackName]) {
          this.callbacks[eventName] = Keyboard.addListener(
            eventName,
            this.props[callbackName]
          )
        }
      })
    }

    componentWillReceiveProps(nextProps: KeyboardAwareHOCProps) {
      if (nextProps.viewIsInsideTabBar !== this.props.viewIsInsideTabBar) {
        const keyboardSpace: number = nextProps.viewIsInsideTabBar
          ? _KAM_DEFAULT_TAB_BAR_HEIGHT
          : 0
        if (this.state.keyboardSpace !== keyboardSpace) {
          this.setState({ keyboardSpace })
        }
      }
    }

    componentWillUnmount() {
      this.mountedComponent = false
      this.keyboardWillShowEvent && this.keyboardWillShowEvent.remove()
      this.keyboardWillHideEvent && this.keyboardWillHideEvent.remove()
      Object.values(this.callbacks).forEach((callback: Object) =>
        callback.remove()
      )
    }

    getScrollResponder = () => {
      return (
        this._rnkasv_keyboardView &&
        this._rnkasv_keyboardView.getScrollResponder()
      )
    }

    scrollToPosition = (x: number, y: number, animated: boolean = true) => {
      const responder = this.getScrollResponder()
      responder && responder.scrollResponderScrollTo({ x, y, animated })
    }

    scrollToEnd = (animated?: boolean = true) => {
      const responder = this.getScrollResponder()
      responder && responder.scrollResponderScrollToEnd({ animated })
    }

    scrollForExtraHeightOnAndroid = (extraHeight: number) => {
      this.scrollToPosition(0, this.position.y + extraHeight, true)
    }

    /**
     * @param keyboardOpeningTime: takes a different keyboardOpeningTime in consideration.
     * @param extraHeight: takes an extra height in consideration.
     */
    scrollToFocusedInput = (
      reactNode: any,
      extraHeight?: number,
      keyboardOpeningTime?: number
    ) => {
      if (extraHeight === undefined) {
        extraHeight = this.props.extraHeight || 0
      }
      if (keyboardOpeningTime === undefined) {
        keyboardOpeningTime = this.props.keyboardOpeningTime || 0
      }
      setTimeout(() => {
        if (!this.mountedComponent) {
          return
        }
        const responder = this.getScrollResponder()
        responder &&
          responder.scrollResponderScrollNativeHandleToKeyboard(
            reactNode,
            extraHeight,
            true
          )
      }, keyboardOpeningTime)
    }

    scrollIntoView = async (
      element: React.Element<*>,
      options: ScrollIntoViewOptions = {}
    ) => {
      if (!this._rnkasv_keyboardView || !element) {
        return
      }

      const [parentLayout, childLayout] = await Promise.all([
        this._measureElement(this._rnkasv_keyboardView),
        this._measureElement(element)
      ])

      const getScrollPosition =
        options.getScrollPosition || this._defaultGetScrollPosition
      const { x, y, animated } = getScrollPosition(
        parentLayout,
        childLayout,
        this.position
      )
      this.scrollToPosition(x, y, animated)
    }

    _defaultGetScrollPosition = (
      parentLayout: ElementLayout,
      childLayout: ElementLayout,
      contentOffset: ContentOffset
    ): ScrollPosition => {
      return {
        x: 0,
        y: Math.max(0, childLayout.y - parentLayout.y + contentOffset.y),
        animated: true
      }
    }

    _measureElement = (element: React.Element<*>): Promise<ElementLayout> => {
      const node = findNodeHandle(element)
      return new Promise((resolve: ElementLayout => void) => {
        UIManager.measureInWindow(
          node,
          (x: number, y: number, width: number, height: number) => {
            resolve({ x, y, width, height })
          }
        )
      })
    }

    // Keyboard actions
    _updateKeyboardSpace = (frames: Object) => {
      // Automatically scroll to focused TextInput
      if (this.props.enableAutomaticScroll) {
        let keyboardSpace: number =
          frames.endCoordinates.height + this.props.extraScrollHeight
        if (this.props.viewIsInsideTabBar) {
          keyboardSpace -= _KAM_DEFAULT_TAB_BAR_HEIGHT
        }
        this.setState({ keyboardSpace })
        const currentlyFocusedField = TextInput.State.currentlyFocusedField()
        const responder = this.getScrollResponder()
        if (!currentlyFocusedField || !responder) {
          return
        }
        UIManager.viewIsDescendantOf(
          currentlyFocusedField,
          responder.getInnerViewNode(),
          (isAncestor: boolean) => {
            if (isAncestor) {
              // Check if the TextInput will be hidden by the keyboard
              UIManager.measureInWindow(
                currentlyFocusedField,
                (x: number, y: number, width: number, height: number) => {
                  const textInputBottomPosition = y + height
                  const keyboardPosition = frames.endCoordinates.screenY
                  const totalExtraHeight =
                    this.props.extraScrollHeight + this.props.extraHeight
                  if (Platform.OS === 'ios') {
                    if (
                      textInputBottomPosition >
                      keyboardPosition - totalExtraHeight
                    ) {
                      this._scrollToFocusedInputWithNodeHandle(
                        currentlyFocusedField
                      )
                    }
                  } else {
                    // On android, the system would scroll the text input just
                    // above the keyboard so we just neet to scroll the extra
                    // height part
                    if (textInputBottomPosition > keyboardPosition) {
                      // Since the system already scrolled the whole view up
                      // we should reduce that amount
                      keyboardSpace =
                        keyboardSpace -
                        (textInputBottomPosition - keyboardPosition)
                      this.setState({ keyboardSpace })
                      this.scrollForExtraHeightOnAndroid(totalExtraHeight)
                    } else if (
                      textInputBottomPosition >
                      keyboardPosition - totalExtraHeight
                    ) {
                      this.scrollForExtraHeightOnAndroid(
                        totalExtraHeight -
                          (keyboardPosition - textInputBottomPosition)
                      )
                    }
                  }
                }
              )
            }
          }
        )
      }
      if (!this.resetCoords) {
        if (!this.defaultResetScrollToCoords) {
          this.defaultResetScrollToCoords = this.position
        }
      }
    }

    _resetKeyboardSpace = () => {
      const keyboardSpace: number = this.props.viewIsInsideTabBar
        ? _KAM_DEFAULT_TAB_BAR_HEIGHT
        : 0
      this.setState({ keyboardSpace })
      // Reset scroll position after keyboard dismissal
      if (this.props.enableResetScrollToCoords === false) {
        this.defaultResetScrollToCoords = null
        return
      } else if (this.resetCoords) {
        this.scrollToPosition(this.resetCoords.x, this.resetCoords.y, true)
      } else {
        if (this.defaultResetScrollToCoords) {
          this.scrollToPosition(
            this.defaultResetScrollToCoords.x,
            this.defaultResetScrollToCoords.y,
            true
          )
          this.defaultResetScrollToCoords = null
        } else {
          this.scrollToPosition(0, 0, true)
        }
      }
    }

    _scrollToFocusedInputWithNodeHandle = (
      nodeID: number,
      extraHeight?: number,
      keyboardOpeningTime?: number
    ) => {
      if (extraHeight === undefined) {
        extraHeight = this.props.extraHeight
      }
      const reactNode = ReactNative.findNodeHandle(nodeID)
      this.scrollToFocusedInput(
        reactNode,
        extraHeight + this.props.extraScrollHeight,
        keyboardOpeningTime !== undefined
          ? keyboardOpeningTime
          : this.props.keyboardOpeningTime || 0
      )
    }

    _handleOnScroll = (
      e: SyntheticEvent<*> & { nativeEvent: { contentOffset: number } }
    ) => {
      this.position = e.nativeEvent.contentOffset
    }

    _handleRef = (ref: React.Component<*>) => {
      this._rnkasv_keyboardView = ref ? hocOptions.extractNativeRef(ref) : ref
      if (this.props.innerRef) {
        this.props.innerRef(this._rnkasv_keyboardView)
      }
    }

    update = () => {
      const currentlyFocusedField = TextInput.State.currentlyFocusedField()
      const responder = this.getScrollResponder()

      if (!currentlyFocusedField || !responder) {
        return
      }

      this._scrollToFocusedInputWithNodeHandle(currentlyFocusedField)
    }

    render() {
      const { enableOnAndroid, contentContainerStyle, onScroll } = this.props
      let newContentContainerStyle
      if (Platform.OS === 'android' && enableOnAndroid) {
        newContentContainerStyle = [].concat(contentContainerStyle).concat({
          paddingBottom:
            ((contentContainerStyle || {}).paddingBottom || 0) +
            this.state.keyboardSpace
        })
      }
      const refProps = { [hocOptions.refPropName]: this._handleRef }
      return (
        <ScrollableComponent
          {...refProps}
          keyboardDismissMode='interactive'
          contentInset={{ bottom: this.state.keyboardSpace }}
          automaticallyAdjustContentInsets={false}
          showsVerticalScrollIndicator={true}
          scrollEventThrottle={1}
          {...this.props}
          contentContainerStyle={
            newContentContainerStyle || contentContainerStyle
          }
          keyboardSpace={this.state.keyboardSpace}
          getScrollResponder={this.getScrollResponder}
          scrollToPosition={this.scrollToPosition}
          scrollToEnd={this.scrollToEnd}
          scrollForExtraHeightOnAndroid={this.scrollForExtraHeightOnAndroid}
          scrollToFocusedInput={this.scrollToFocusedInput}
          scrollIntoView={this.scrollIntoView}
          resetKeyboardSpace={this._resetKeyboardSpace}
          handleOnScroll={this._handleOnScroll}
          update={this.update}
          onScroll={Animated.forkEvent(onScroll, this._handleOnScroll)}
        />
      )
    }
  }
}

// Allow to pass options, without breaking change, and curried for composition
// listenToKeyboardEvents(ScrollView);
// listenToKeyboardEvents(options)(Comp);
const listenToKeyboardEvents = (configOrComp: any) => {
  if (typeof configOrComp === 'object') {
    return (Comp: Function) => KeyboardAwareHOC(Comp, configOrComp)
  } else {
    return KeyboardAwareHOC(configOrComp)
  }
}

export default listenToKeyboardEvents
<commit_msg>Fix for Issue #320 (#324)<commit_after>/* @flow */

import React from 'react'
import PropTypes from 'prop-types'
import ReactNative, {
  Keyboard,
  Platform,
  UIManager,
  TextInput,
  findNodeHandle,
  Animated
} from 'react-native'
import { isIphoneX } from 'react-native-iphone-x-helper'
import type { KeyboardAwareInterface } from './KeyboardAwareInterface'

const _KAM_DEFAULT_TAB_BAR_HEIGHT: number = isIphoneX() ? 83 : 49
const _KAM_KEYBOARD_OPENING_TIME: number = 250
const _KAM_EXTRA_HEIGHT: number = 75

const supportedKeyboardEvents = [
  'keyboardWillShow',
  'keyboardDidShow',
  'keyboardWillHide',
  'keyboardDidHide',
  'keyboardWillChangeFrame',
  'keyboardDidChangeFrame'
]
const keyboardEventToCallbackName = (eventName: string) =>
  'on' + eventName[0].toUpperCase() + eventName.substring(1)
const keyboardEventPropTypes = supportedKeyboardEvents.reduce(
  (acc: Object, eventName: string) => ({
    ...acc,
    [keyboardEventToCallbackName(eventName)]: PropTypes.func
  }),
  {}
)
const keyboardAwareHOCTypeEvents = supportedKeyboardEvents.reduce(
  (acc: Object, eventName: string) => ({
    ...acc,
    [keyboardEventToCallbackName(eventName)]: Function
  }),
  {}
)

export type KeyboardAwareHOCProps = {
  viewIsInsideTabBar?: boolean,
  resetScrollToCoords?: {
    x: number,
    y: number
  },
  enableResetScrollToCoords?: boolean,
  enableAutomaticScroll?: boolean,
  extraHeight?: number,
  extraScrollHeight?: number,
  keyboardOpeningTime?: number,
  onScroll?: Function,
  update?: Function,
  contentContainerStyle?: any,
  enableOnAndroid?: boolean,
  innerRef?: Function,
  ...keyboardAwareHOCTypeEvents
}
export type KeyboardAwareHOCState = {
  keyboardSpace: number
}

export type ElementLayout = {
  x: number,
  y: number,
  width: number,
  height: number
}

export type ContentOffset = {
  x: number,
  y: number
}

export type ScrollPosition = {
  x: number,
  y: number,
  animated: boolean
}

export type ScrollIntoViewOptions = ?{
  getScrollPosition?: (
    parentLayout: ElementLayout,
    childLayout: ElementLayout,
    contentOffset: ContentOffset
  ) => ScrollPosition
}

export type KeyboardAwareHOCOptions = ?{
  enableOnAndroid: boolean,
  contentContainerStyle: ?Object,
  enableAutomaticScroll: boolean,
  extraHeight: number,
  extraScrollHeight: number,
  enableResetScrollToCoords: boolean,
  keyboardOpeningTime: number,
  viewIsInsideTabBar: boolean,
  refPropName: string,
  extractNativeRef: Function
}

function getDisplayName(WrappedComponent: React$Component) {
  return WrappedComponent && (WrappedComponent.displayName || WrappedComponent.name) || 'Component'
}

const ScrollIntoViewDefaultOptions: KeyboardAwareHOCOptions = {
  enableOnAndroid: false,
  contentContainerStyle: undefined,
  enableAutomaticScroll: true,
  extraHeight: _KAM_EXTRA_HEIGHT,
  extraScrollHeight: 0,
  enableResetScrollToCoords: true,
  keyboardOpeningTime: _KAM_KEYBOARD_OPENING_TIME,
  viewIsInsideTabBar: false,

  // The ref prop name that will be passed to the wrapped component to obtain a ref
  // If your ScrollView is already wrapped, maybe the wrapper permit to get a ref
  // For example, with glamorous-native ScrollView, you should use "innerRef"
  refPropName: 'ref',
  // Sometimes the ref you get is a ref to a wrapped view (ex: Animated.ScrollView)
  // We need access to the imperative API of a real native ScrollView so we need extraction logic
  extractNativeRef: (ref: Object) => {
    // getNode() permit to support Animated.ScrollView automatically
    // see https://github.com/facebook/react-native/issues/19650
    // see https://stackoverflow.com/questions/42051368/scrollto-is-undefined-on-animated-scrollview/48786374
    if (ref.getNode) {
      return ref.getNode()
    } else {
      return ref
    }
  }
}

function KeyboardAwareHOC(
  ScrollableComponent: React$Component,
  userOptions: KeyboardAwareHOCOptions
) {
  const hocOptions: KeyboardAwareHOCOptions = {
    ...ScrollIntoViewDefaultOptions,
    ...userOptions
  }

  return class
    extends React.Component<KeyboardAwareHOCProps, KeyboardAwareHOCState>
    implements KeyboardAwareInterface {
    _rnkasv_keyboardView: any
    keyboardWillShowEvent: ?Function
    keyboardWillHideEvent: ?Function
    position: ContentOffset
    defaultResetScrollToCoords: ?{ x: number, y: number }
    resetCoords: ?{ x: number, y: number }
    mountedComponent: boolean
    handleOnScroll: Function
    state: KeyboardAwareHOCState
    static displayName = `KeyboardAware${getDisplayName(ScrollableComponent)}`

    static propTypes = {
      viewIsInsideTabBar: PropTypes.bool,
      resetScrollToCoords: PropTypes.shape({
        x: PropTypes.number.isRequired,
        y: PropTypes.number.isRequired
      }),
      enableResetScrollToCoords: PropTypes.bool,
      enableAutomaticScroll: PropTypes.bool,
      extraHeight: PropTypes.number,
      extraScrollHeight: PropTypes.number,
      keyboardOpeningTime: PropTypes.number,
      onScroll: PropTypes.oneOfType([
        PropTypes.func, // Normal listener
        PropTypes.object // Animated.event listener
      ]),
      update: PropTypes.func,
      contentContainerStyle: PropTypes.any,
      enableOnAndroid: PropTypes.bool,
      innerRef: PropTypes.func,
      ...keyboardEventPropTypes
    }

    // HOC options are used to init default props, so that these options can be overriden with component props
    static defaultProps = {
      enableAutomaticScroll: hocOptions.enableAutomaticScroll,
      extraHeight: hocOptions.extraHeight,
      extraScrollHeight: hocOptions.extraScrollHeight,
      enableResetScrollToCoords: hocOptions.enableResetScrollToCoords,
      keyboardOpeningTime: hocOptions.keyboardOpeningTime,
      viewIsInsideTabBar: hocOptions.viewIsInsideTabBar,
      enableOnAndroid: hocOptions.enableOnAndroid
    }

    constructor(props: KeyboardAwareHOCProps) {
      super(props)
      this.keyboardWillShowEvent = undefined
      this.keyboardWillHideEvent = undefined
      this.callbacks = {}
      this.position = { x: 0, y: 0 }
      this.defaultResetScrollToCoords = null
      const keyboardSpace: number = props.viewIsInsideTabBar
        ? _KAM_DEFAULT_TAB_BAR_HEIGHT
        : 0
      this.state = { keyboardSpace }
    }

    componentDidMount() {
      this.mountedComponent = true
      // Keyboard events
      if (Platform.OS === 'ios') {
        this.keyboardWillShowEvent = Keyboard.addListener(
          'keyboardWillShow',
          this._updateKeyboardSpace
        )
        this.keyboardWillHideEvent = Keyboard.addListener(
          'keyboardWillHide',
          this._resetKeyboardSpace
        )
      } else if (Platform.OS === 'android' && this.props.enableOnAndroid) {
        this.keyboardWillShowEvent = Keyboard.addListener(
          'keyboardDidShow',
          this._updateKeyboardSpace
        )
        this.keyboardWillHideEvent = Keyboard.addListener(
          'keyboardDidHide',
          this._resetKeyboardSpace
        )
      }

      supportedKeyboardEvents.forEach((eventName: string) => {
        const callbackName = keyboardEventToCallbackName(eventName)
        if (this.props[callbackName]) {
          this.callbacks[eventName] = Keyboard.addListener(
            eventName,
            this.props[callbackName]
          )
        }
      })
    }

    componentWillReceiveProps(nextProps: KeyboardAwareHOCProps) {
      if (nextProps.viewIsInsideTabBar !== this.props.viewIsInsideTabBar) {
        const keyboardSpace: number = nextProps.viewIsInsideTabBar
          ? _KAM_DEFAULT_TAB_BAR_HEIGHT
          : 0
        if (this.state.keyboardSpace !== keyboardSpace) {
          this.setState({ keyboardSpace })
        }
      }
    }

    componentWillUnmount() {
      this.mountedComponent = false
      this.keyboardWillShowEvent && this.keyboardWillShowEvent.remove()
      this.keyboardWillHideEvent && this.keyboardWillHideEvent.remove()
      Object.values(this.callbacks).forEach((callback: Object) =>
        callback.remove()
      )
    }

    getScrollResponder = () => {
      return (
        this._rnkasv_keyboardView &&
        this._rnkasv_keyboardView.getScrollResponder()
      )
    }

    scrollToPosition = (x: number, y: number, animated: boolean = true) => {
      const responder = this.getScrollResponder()
      responder && responder.scrollResponderScrollTo({ x, y, animated })
    }

    scrollToEnd = (animated?: boolean = true) => {
      const responder = this.getScrollResponder()
      responder && responder.scrollResponderScrollToEnd({ animated })
    }

    scrollForExtraHeightOnAndroid = (extraHeight: number) => {
      this.scrollToPosition(0, this.position.y + extraHeight, true)
    }

    /**
     * @param keyboardOpeningTime: takes a different keyboardOpeningTime in consideration.
     * @param extraHeight: takes an extra height in consideration.
     */
    scrollToFocusedInput = (
      reactNode: any,
      extraHeight?: number,
      keyboardOpeningTime?: number
    ) => {
      if (extraHeight === undefined) {
        extraHeight = this.props.extraHeight || 0
      }
      if (keyboardOpeningTime === undefined) {
        keyboardOpeningTime = this.props.keyboardOpeningTime || 0
      }
      setTimeout(() => {
        if (!this.mountedComponent) {
          return
        }
        const responder = this.getScrollResponder()
        responder &&
          responder.scrollResponderScrollNativeHandleToKeyboard(
            reactNode,
            extraHeight,
            true
          )
      }, keyboardOpeningTime)
    }

    scrollIntoView = async (
      element: React.Element<*>,
      options: ScrollIntoViewOptions = {}
    ) => {
      if (!this._rnkasv_keyboardView || !element) {
        return
      }

      const [parentLayout, childLayout] = await Promise.all([
        this._measureElement(this._rnkasv_keyboardView),
        this._measureElement(element)
      ])

      const getScrollPosition =
        options.getScrollPosition || this._defaultGetScrollPosition
      const { x, y, animated } = getScrollPosition(
        parentLayout,
        childLayout,
        this.position
      )
      this.scrollToPosition(x, y, animated)
    }

    _defaultGetScrollPosition = (
      parentLayout: ElementLayout,
      childLayout: ElementLayout,
      contentOffset: ContentOffset
    ): ScrollPosition => {
      return {
        x: 0,
        y: Math.max(0, childLayout.y - parentLayout.y + contentOffset.y),
        animated: true
      }
    }

    _measureElement = (element: React.Element<*>): Promise<ElementLayout> => {
      const node = findNodeHandle(element)
      return new Promise((resolve: ElementLayout => void) => {
        UIManager.measureInWindow(
          node,
          (x: number, y: number, width: number, height: number) => {
            resolve({ x, y, width, height })
          }
        )
      })
    }

    // Keyboard actions
    _updateKeyboardSpace = (frames: Object) => {
      // Automatically scroll to focused TextInput
      if (this.props.enableAutomaticScroll) {
        let keyboardSpace: number =
          frames.endCoordinates.height + this.props.extraScrollHeight
        if (this.props.viewIsInsideTabBar) {
          keyboardSpace -= _KAM_DEFAULT_TAB_BAR_HEIGHT
        }
        this.setState({ keyboardSpace })
        const currentlyFocusedField = TextInput.State.currentlyFocusedField()
        const responder = this.getScrollResponder()
        if (!currentlyFocusedField || !responder) {
          return
        }
        UIManager.viewIsDescendantOf(
          currentlyFocusedField,
          responder.getInnerViewNode(),
          (isAncestor: boolean) => {
            if (isAncestor) {
              // Check if the TextInput will be hidden by the keyboard
              UIManager.measureInWindow(
                currentlyFocusedField,
                (x: number, y: number, width: number, height: number) => {
                  const textInputBottomPosition = y + height
                  const keyboardPosition = frames.endCoordinates.screenY
                  const totalExtraHeight =
                    this.props.extraScrollHeight + this.props.extraHeight
                  if (Platform.OS === 'ios') {
                    if (
                      textInputBottomPosition >
                      keyboardPosition - totalExtraHeight
                    ) {
                      this._scrollToFocusedInputWithNodeHandle(
                        currentlyFocusedField
                      )
                    }
                  } else {
                    // On android, the system would scroll the text input just
                    // above the keyboard so we just neet to scroll the extra
                    // height part
                    if (textInputBottomPosition > keyboardPosition) {
                      // Since the system already scrolled the whole view up
                      // we should reduce that amount
                      keyboardSpace =
                        keyboardSpace -
                        (textInputBottomPosition - keyboardPosition)
                      this.setState({ keyboardSpace })
                      this.scrollForExtraHeightOnAndroid(totalExtraHeight)
                    } else if (
                      textInputBottomPosition >
                      keyboardPosition - totalExtraHeight
                    ) {
                      this.scrollForExtraHeightOnAndroid(
                        totalExtraHeight -
                          (keyboardPosition - textInputBottomPosition)
                      )
                    }
                  }
                }
              )
            }
          }
        )
      }
      if (!this.resetCoords) {
        if (!this.defaultResetScrollToCoords) {
          this.defaultResetScrollToCoords = this.position
        }
      }
    }

    _resetKeyboardSpace = () => {
      const keyboardSpace: number = this.props.viewIsInsideTabBar
        ? _KAM_DEFAULT_TAB_BAR_HEIGHT
        : 0
      this.setState({ keyboardSpace })
      // Reset scroll position after keyboard dismissal
      if (this.props.enableResetScrollToCoords === false) {
        this.defaultResetScrollToCoords = null
        return
      } else if (this.resetCoords) {
        this.scrollToPosition(this.resetCoords.x, this.resetCoords.y, true)
      } else {
        if (this.defaultResetScrollToCoords) {
          this.scrollToPosition(
            this.defaultResetScrollToCoords.x,
            this.defaultResetScrollToCoords.y,
            true
          )
          this.defaultResetScrollToCoords = null
        } else {
          this.scrollToPosition(0, 0, true)
        }
      }
    }

    _scrollToFocusedInputWithNodeHandle = (
      nodeID: number,
      extraHeight?: number,
      keyboardOpeningTime?: number
    ) => {
      if (extraHeight === undefined) {
        extraHeight = this.props.extraHeight
      }
      const reactNode = ReactNative.findNodeHandle(nodeID)
      this.scrollToFocusedInput(
        reactNode,
        extraHeight + this.props.extraScrollHeight,
        keyboardOpeningTime !== undefined
          ? keyboardOpeningTime
          : this.props.keyboardOpeningTime || 0
      )
    }

    _handleOnScroll = (
      e: SyntheticEvent<*> & { nativeEvent: { contentOffset: number } }
    ) => {
      this.position = e.nativeEvent.contentOffset
    }

    _handleRef = (ref: React.Component<*>) => {
      this._rnkasv_keyboardView = ref ? hocOptions.extractNativeRef(ref) : ref
      if (this.props.innerRef) {
        this.props.innerRef(this._rnkasv_keyboardView)
      }
    }

    update = () => {
      const currentlyFocusedField = TextInput.State.currentlyFocusedField()
      const responder = this.getScrollResponder()

      if (!currentlyFocusedField || !responder) {
        return
      }

      this._scrollToFocusedInputWithNodeHandle(currentlyFocusedField)
    }

    render() {
      const { enableOnAndroid, contentContainerStyle, onScroll } = this.props
      let newContentContainerStyle
      if (Platform.OS === 'android' && enableOnAndroid) {
        newContentContainerStyle = [].concat(contentContainerStyle).concat({
          paddingBottom:
            ((contentContainerStyle || {}).paddingBottom || 0) +
            this.state.keyboardSpace
        })
      }
      const refProps = { [hocOptions.refPropName]: this._handleRef }
      return (
        <ScrollableComponent
          {...refProps}
          keyboardDismissMode='interactive'
          contentInset={{ bottom: this.state.keyboardSpace }}
          automaticallyAdjustContentInsets={false}
          showsVerticalScrollIndicator={true}
          scrollEventThrottle={1}
          {...this.props}
          contentContainerStyle={
            newContentContainerStyle || contentContainerStyle
          }
          keyboardSpace={this.state.keyboardSpace}
          getScrollResponder={this.getScrollResponder}
          scrollToPosition={this.scrollToPosition}
          scrollToEnd={this.scrollToEnd}
          scrollForExtraHeightOnAndroid={this.scrollForExtraHeightOnAndroid}
          scrollToFocusedInput={this.scrollToFocusedInput}
          scrollIntoView={this.scrollIntoView}
          resetKeyboardSpace={this._resetKeyboardSpace}
          handleOnScroll={this._handleOnScroll}
          update={this.update}
          onScroll={Animated.forkEvent(onScroll, this._handleOnScroll)}
        />
      )
    }
  }
}

// Allow to pass options, without breaking change, and curried for composition
// listenToKeyboardEvents(ScrollView);
// listenToKeyboardEvents(options)(Comp);
const listenToKeyboardEvents = (configOrComp: any) => {
  if (typeof configOrComp === 'object') {
    return (Comp: Function) => KeyboardAwareHOC(Comp, configOrComp)
  } else {
    return KeyboardAwareHOC(configOrComp)
  }
}

export default listenToKeyboardEvents
<|endoftext|>


<commit_before>module.exports = {
    logo: 'src/assets/protonaccount.svg',
    favicons: {
        appName: 'Proton Account',
        appDescription:
            "Proton Mail is the world's largest secure email service, developed by CERN and MIT scientists. We are open source and protected by Swiss privacy law",
    },
};
<commit_msg>Remove unused favicon config from storybook<commit_after><|endoftext|>


<commit_before>$(document).ready(function () {
    $(document).foundation();
    $.ajaxSetup({
        xhrFields: {
            withCredentials: true
        }
    });

    // image lazy loading
    $.fn.unveil = function(threshold, callback) {

        var $w = $(window),
            th = threshold || 0,
            retina = window.devicePixelRatio > 1,
            attrib = retina? "data-src-retina" : "data-src",
            images = this,
            loaded;

        this.one("unveil", function() {
          var source = this.getAttribute(attrib);
          source = source || this.getAttribute("data-src");
          if (source) {
            this.setAttribute("src", source);
            if (typeof callback === "function") callback.call(this);
          }
        });

        function unveil() {
          var inview = images.filter(function() {
            var $e = $(this);
            if ($e.is(":hidden")) return;
            return true;
          });

          loaded = inview.trigger("unveil");
          images = images.not(loaded);
        }

        $w.on("scroll.unveil resize.unveil lookup.unveil", unveil);

        unveil();

        return this;

    };

    // force all external links to open in a new tab
    $('a[href^="http"]:not([href*="'+document.location.hostname+'"])').attr("target","_blank");
    // hack for relative links in wagtail to convert /./#anchortag to just #anchortag
    $('a[href*="/./#"]').each(function() {
        var href = $(this).attr("href").replace("/./#", "#");
        $(this).attr("href", href);
    });

    $.urlParam = function(name, url) {
        if (!url) {
         url = window.location.href;
        }
        var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(url);
        if (!results) {
            return undefined;
        }
        return results[1] || undefined;
    }

    // On draft pages highlight top and add edit link, also adjust links to also point to drafts
    if (document.location.pathname.search("/admin/pages") == 0) {
        $('a[href^="/"]').each(function() {
            $(this).attr("href", "/draft"+$(this).attr("href"));
        });
        $(".draft-bg-change").css({"background-color":"rgb(252, 175, 62)"});
        $(".draft-content").html("
Draft Preview (Edit)
");
    }

    // unbreak wagtail user bar
    window.fixwtub = setInterval(function() {
        $("#wagtail-userbar").removeAttr("style").length && clearInterval(fixwtub);
    }, 100);

    window.loadDatalist = function(listid, source) {
        if ($("#"+listid).length == 1) { return; }
        if (localStorage.getItem("list_"+listid)) {
            var datalist = $("<datalist/>", {id: listid});
            $.each(JSON.parse(localStorage.getItem("list_"+listid)), function(index, value) {
                $("<option/>").text(value).appendTo(datalist);
            });
            $("body").append(datalist);
        }
        if (source) {
            $.get(source, function(data) {
                localStorage.setItem("list_"+listid, JSON.stringify($.map(data, function(i) { return i[Object.keys(i)] })));
                loadDatalist(listid);
            });
        } else {
            $.get("/api/options?list=" + listid, function(data) {
                localStorage.setItem("list_"+listid, JSON.stringify(data.objects));
                loadDatalist(listid);
            });
        }
    }

    // form upgrades
    window.upgradeForms = function() {
        $("input[type=date]").fdatepicker({format: "dd/mm/yyyy"}).attr({placeholder: "dd/mm/yyyy"});
        // Find all inputs on the DOM which are bound to a datalist via their list attribute.
        $('input[list]').on("input", function() {
            // use the setCustomValidity function of the Validation API
            // to provide an user feedback if the value does not exist in the datalist
            var value = this.value;
            if ($.grep(this.list.options, function(opt) {return opt.text == value}).length == 1) {
              this.setCustomValidity('');
            } else {
              this.setCustomValidity('Please select a valid value.');
            }
        }).each(function() {
            if (this.list == null) { loadDatalist($(this).attr("list"), $(this).attr("data-src")); }
        });
    }

    $(document).ready(upgradeForms);

    window._renderHandlebars = function(tmpl, data, callback) {
        tmpl.after(data);
        window[callback]();
    }

    window.renderHandlebars = function() {
        $("script[data-url]").each(function() {
            var tmpl = $(this);
            var url = tmpl.attr("data-url");
            var callback = tmpl.attr("data-onload");
            localforage.getItem(url).then(function(data) {
                if (data) { _renderHandlebars(tmpl, data, callback) }
                $.get(url, function(rawdata) {
                    var comp_data = Handlebars.compile(tmpl.html())(rawdata);
                    localforage.setItem(url, comp_data);
                    if (!data) { _renderHandlebars(tmpl, comp_data, callback) }
                });
            });
        });
    }

    $(document).ready(renderHandlebars);

    var egg = new Egg("up,up,down,down,left,right,left,right,b,a", function() {
        s=document.createElement('script');
        s.src="//static.dpaw.wa.gov.au/static/js/kh.js";
        document.body.appendChild(s)
    }).listen();
});
<commit_msg>only use jquery datepicker if there's no browser support<commit_after>$(document).ready(function () {
    $(document).foundation();
    $.ajaxSetup({
        xhrFields: {
            withCredentials: true
        }
    });

    // image lazy loading
    $.fn.unveil = function(threshold, callback) {

        var $w = $(window),
            th = threshold || 0,
            retina = window.devicePixelRatio > 1,
            attrib = retina? "data-src-retina" : "data-src",
            images = this,
            loaded;

        this.one("unveil", function() {
          var source = this.getAttribute(attrib);
          source = source || this.getAttribute("data-src");
          if (source) {
            this.setAttribute("src", source);
            if (typeof callback === "function") callback.call(this);
          }
        });

        function unveil() {
          var inview = images.filter(function() {
            var $e = $(this);
            if ($e.is(":hidden")) return;
            return true;
          });

          loaded = inview.trigger("unveil");
          images = images.not(loaded);
        }

        $w.on("scroll.unveil resize.unveil lookup.unveil", unveil);

        unveil();

        return this;

    };

    // force all external links to open in a new tab
    $('a[href^="http"]:not([href*="'+document.location.hostname+'"])').attr("target","_blank");
    // hack for relative links in wagtail to convert /./#anchortag to just #anchortag
    $('a[href*="/./#"]').each(function() {
        var href = $(this).attr("href").replace("/./#", "#");
        $(this).attr("href", href);
    });

    $.urlParam = function(name, url) {
        if (!url) {
         url = window.location.href;
        }
        var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(url);
        if (!results) {
            return undefined;
        }
        return results[1] || undefined;
    }

    // On draft pages highlight top and add edit link, also adjust links to also point to drafts
    if (document.location.pathname.search("/admin/pages") == 0) {
        $('a[href^="/"]').each(function() {
            $(this).attr("href", "/draft"+$(this).attr("href"));
        });
        $(".draft-bg-change").css({"background-color":"rgb(252, 175, 62)"});
        $(".draft-content").html("
Draft Preview (Edit)
");
    }

    // unbreak wagtail user bar
    window.fixwtub = setInterval(function() {
        $("#wagtail-userbar").removeAttr("style").length && clearInterval(fixwtub);
    }, 100);

    window.loadDatalist = function(listid, source) {
        if ($("#"+listid).length == 1) { return; }
        if (localStorage.getItem("list_"+listid)) {
            var datalist = $("<datalist/>", {id: listid});
            $.each(JSON.parse(localStorage.getItem("list_"+listid)), function(index, value) {
                $("<option/>").text(value).appendTo(datalist);
            });
            $("body").append(datalist);
        }
        if (source) {
            $.get(source, function(data) {
                localStorage.setItem("list_"+listid, JSON.stringify($.map(data, function(i) { return i[Object.keys(i)] })));
                loadDatalist(listid);
            });
        } else {
            $.get("/api/options?list=" + listid, function(data) {
                localStorage.setItem("list_"+listid, JSON.stringify(data.objects));
                loadDatalist(listid);
            });
        }
    }

    // form upgrades
    window.upgradeForms = function() {
        if (!Modernizr.inputtypes.date) {
            $("input[type=date]").fdatepicker({format: "dd/mm/yyyy"}).attr({placeholder: "dd/mm/yyyy"});
        }
        // Find all inputs on the DOM which are bound to a datalist via their list attribute.
        $('input[list]').on("input", function() {
            // use the setCustomValidity function of the Validation API
            // to provide an user feedback if the value does not exist in the datalist
            var value = this.value;
            if ($.grep(this.list.options, function(opt) {return opt.text == value}).length == 1) {
              this.setCustomValidity('');
            } else {
              this.setCustomValidity('Please select a valid value.');
            }
        }).each(function() {
            if (this.list == null) { loadDatalist($(this).attr("list"), $(this).attr("data-src")); }
        });
    }

    $(document).ready(upgradeForms);

    window._renderHandlebars = function(tmpl, data, callback) {
        tmpl.after(data);
        window[callback]();
    }

    window.renderHandlebars = function() {
        $("script[data-url]").each(function() {
            var tmpl = $(this);
            var url = tmpl.attr("data-url");
            var callback = tmpl.attr("data-onload");
            localforage.getItem(url).then(function(data) {
                if (data) { _renderHandlebars(tmpl, data, callback) }
                $.get(url, function(rawdata) {
                    var comp_data = Handlebars.compile(tmpl.html())(rawdata);
                    localforage.setItem(url, comp_data);
                    if (!data) { _renderHandlebars(tmpl, comp_data, callback) }
                });
            });
        });
    }

    $(document).ready(renderHandlebars);

    var egg = new Egg("up,up,down,down,left,right,left,right,b,a", function() {
        s=document.createElement('script');
        s.src="//static.dpaw.wa.gov.au/static/js/kh.js";
        document.body.appendChild(s)
    }).listen();
});
<|endoftext|>


<commit_before>"use strict";

class gsuiReorder {
	constructor( opt = {} ) {
		const root = opt.rootElement;

		this.rootElement = root;
		this._dirRow = opt.direction !== "column";
		this._onchange = opt.onchange ?? ( () => console.warn( "gsuiReorder: no onchange set" ) );
		this._dataTransfer = opt.dataTransfer ?? ( () => "" );
		this._dataTransferType = opt.dataTransferType ?? "text";
		this._itemSelector = opt.itemSelector ?? "";
		this._handleSelector = opt.handleSelector ?? "";
		this._parentSelector = opt.parentSelector ?? "";
		this._preventDefault = opt.preventDefault ?? true;
		this._elClicked =
		this._elDragged =
		this._elDragover =
		this._itemDragover =
		this._parentDragover =
		this._elShadowParent =
		this._elShadowDragged =
		this._elDraggedParent = null;
		this._shadowClass = "";
		this._indDragged = 0;
		this._droppedInside = false;
		this._ondrop = this._ondrop.bind( this );
		this._dragoverTime = 0;
		Object.seal( this );

		root.addEventListener( "mousedown", this._onmousedown.bind( this ), { passive: true } );
		root.addEventListener( "dragstart", this._ondragstart.bind( this ), { passive: false } );
		root.addEventListener( "dragover", this._ondragover.bind( this ), { passive: true } );
		root.addEventListener( "dragend", this._ondragend.bind( this ), { passive: true } );
	}

	setShadowElement( el ) {
		this._elShadowParent = el;
	}
	setShadowChildClass( cl ) {
		this._shadowClass = `.${ cl }`;
	}

	// private:
	// .........................................................................
	_getIndex( el ) {
		return Array.prototype.indexOf.call( el.parentNode.children, el );
	}
	_getShadowChild( id ) {
		return this._elShadowParent.querySelector( `${ this._shadowClass }[data-id="${ id }"]` );
	}

	// events:
	// .........................................................................
	_onmousedown( e ) {
		this._elClicked = e.target;
	}
	_ondragstart( e ) {
		if ( this._elClicked && this._elClicked.matches( this._handleSelector ) ) {
			const elItem = e.target,
				itemId = elItem.dataset.id;

			document.addEventListener( "drop", this._ondrop );
			this._elClicked = null;
			this._elDragged = elItem;
			this._elDraggedParent = elItem.parentNode;
			if ( this._elShadowParent ) {
				this._elShadowDragged = this._getShadowChild( itemId );
			}
			this._indDragged = this._getIndex( elItem );
			e.dataTransfer.effectAllowed = "move";
			e.dataTransfer.setData( this._dataTransferType, this._dataTransfer( elItem ) );
			setTimeout( () => {
				this._elDragged.classList.add( "gsuiReorder-dragging" );
				if ( this._elShadowDragged ) {
					this._elShadowDragged.classList.add( "gsuiReorder-dragging" );
				}
			}, 20 );
		} else if ( this._preventDefault ) {
			e.preventDefault();
		}
	}
	_ondragover( e ) {
		const now = Date.now();

		if ( this._elDragged && now - this._dragoverTime > 60 ) {
			const tar = e.target,
				elDrag = this._elDragged,
				elOver = tar === this._elDragover
					? this._itemDragover
					: tar.closest( `${ this._parentSelector } ${ this._itemSelector }` );

			this._dragoverTime = now;
			if ( !elOver ) {
				const elOver = tar === this._elDragover
						? this._parentDragover
						: tar.closest( this._parentSelector );

				this._parentDragover = elOver;
				if ( elOver && elOver.lastElementChild !== elDrag ) {
					elOver.append( elDrag );
				}
			} else {
				const bcr = elOver.getBoundingClientRect();

				if ( ( this._dirRow && e.clientX < bcr.left + bcr.width / 2 ) ||
					( !this._dirRow && e.clientY < bcr.top + bcr.height / 2 )
				) {
					if ( elOver.previousElementSibling !== elDrag ) {
						elOver.before( elDrag );
						if ( this._elShadowDragged ) {
							this._getShadowChild( elOver.dataset.id ).before( this._elShadowDragged );
						}
					}
				} else if ( elOver.nextElementSibling !== elDrag ) {
					elOver.after( elDrag );
					if ( this._elShadowDragged ) {
						this._getShadowChild( elOver.dataset.id ).after( this._elShadowDragged );
					}
				}
			}
			this._elDragover = tar;
			this._itemDragover = elOver;
		}
	}
	_ondrop( e ) {
		this._droppedInside = this._elDragged && e.target.closest( this._parentSelector );
	}
	_ondragend() {
		if ( this._elDragged ) {
			const el = this._elDragged,
				oldInd = this._indDragged,
				oldPar = this._elDraggedParent;

			this._elDragged =
			this._elDragover =
			this._itemDragover = null;
			el.classList.remove( "gsuiReorder-dragging" );
			if ( this._elShadowDragged ) {
				this._elShadowDragged.classList.remove( "gsuiReorder-dragging" );
			}
			document.removeEventListener( "drop", this._ondrop );
			if ( this._droppedInside ) {
				const ind = this._getIndex( el );

				this._droppedInside = false;
				if ( ind !== oldInd || el.parentNode !== oldPar ) {
					this._onchange( el, oldInd, ind, oldPar, el.parentNode );
				}
			} else {
				el.remove();
				oldInd === 0
					? oldPar.prepend( el )
					: oldPar.children[ oldInd - 1 ].after( el );
			}
		}
	}
}
<commit_msg>gsuiReorder: fix, if the item is dragging over the parent then the shadow element will be sync<commit_after>"use strict";

class gsuiReorder {
	constructor( opt = {} ) {
		const root = opt.rootElement;

		this.rootElement = root;
		this._dirRow = opt.direction !== "column";
		this._onchange = opt.onchange ?? ( () => console.warn( "gsuiReorder: no onchange set" ) );
		this._dataTransfer = opt.dataTransfer ?? ( () => "" );
		this._dataTransferType = opt.dataTransferType ?? "text";
		this._itemSelector = opt.itemSelector ?? "";
		this._handleSelector = opt.handleSelector ?? "";
		this._parentSelector = opt.parentSelector ?? "";
		this._preventDefault = opt.preventDefault ?? true;
		this._elClicked =
		this._elDragged =
		this._elDragover =
		this._itemDragover =
		this._parentDragover =
		this._elShadowParent =
		this._elShadowDragged =
		this._elDraggedParent = null;
		this._shadowClass = "";
		this._indDragged = 0;
		this._droppedInside = false;
		this._ondrop = this._ondrop.bind( this );
		this._dragoverTime = 0;
		Object.seal( this );

		root.addEventListener( "mousedown", this._onmousedown.bind( this ), { passive: true } );
		root.addEventListener( "dragstart", this._ondragstart.bind( this ), { passive: false } );
		root.addEventListener( "dragover", this._ondragover.bind( this ), { passive: true } );
		root.addEventListener( "dragend", this._ondragend.bind( this ), { passive: true } );
	}

	setShadowElement( el ) {
		this._elShadowParent = el;
	}
	setShadowChildClass( cl ) {
		this._shadowClass = `.${ cl }`;
	}

	// private:
	// .........................................................................
	_getIndex( el ) {
		return Array.prototype.indexOf.call( el.parentNode.children, el );
	}
	_getShadowChild( id ) {
		return this._elShadowParent.querySelector( `${ this._shadowClass }[data-id="${ id }"]` );
	}

	// events:
	// .........................................................................
	_onmousedown( e ) {
		this._elClicked = e.target;
	}
	_ondragstart( e ) {
		if ( this._elClicked && this._elClicked.matches( this._handleSelector ) ) {
			const elItem = e.target,
				itemId = elItem.dataset.id;

			document.addEventListener( "drop", this._ondrop );
			this._elClicked = null;
			this._elDragged = elItem;
			this._elDraggedParent = elItem.parentNode;
			if ( this._elShadowParent ) {
				this._elShadowDragged = this._getShadowChild( itemId );
			}
			this._indDragged = this._getIndex( elItem );
			e.dataTransfer.effectAllowed = "move";
			e.dataTransfer.setData( this._dataTransferType, this._dataTransfer( elItem ) );
			setTimeout( () => {
				this._elDragged.classList.add( "gsuiReorder-dragging" );
				if ( this._elShadowDragged ) {
					this._elShadowDragged.classList.add( "gsuiReorder-dragging" );
				}
			}, 20 );
		} else if ( this._preventDefault ) {
			e.preventDefault();
		}
	}
	_ondragover( e ) {
		const now = Date.now();

		if ( this._elDragged && now - this._dragoverTime > 60 ) {
			const tar = e.target,
				elDrag = this._elDragged,
				elOver = tar === this._elDragover
					? this._itemDragover
					: tar.closest( `${ this._parentSelector } ${ this._itemSelector }` );

			this._dragoverTime = now;
			if ( !elOver ) {
				const elOver = tar === this._elDragover
						? this._parentDragover
						: tar.closest( this._parentSelector );

				this._parentDragover = elOver;
				if ( elOver && elOver.lastElementChild !== elDrag ) {
					elOver.append( elDrag );
					if ( this._elShadowDragged ) {
						this._elShadowParent.append( this._elShadowDragged );
					}
				}
			} else {
				const bcr = elOver.getBoundingClientRect();

				if ( ( this._dirRow && e.clientX < bcr.left + bcr.width / 2 ) ||
					( !this._dirRow && e.clientY < bcr.top + bcr.height / 2 )
				) {
					if ( elOver.previousElementSibling !== elDrag ) {
						elOver.before( elDrag );
						if ( this._elShadowDragged ) {
							this._getShadowChild( elOver.dataset.id ).before( this._elShadowDragged );
						}
					}
				} else if ( elOver.nextElementSibling !== elDrag ) {
					elOver.after( elDrag );
					if ( this._elShadowDragged ) {
						this._getShadowChild( elOver.dataset.id ).after( this._elShadowDragged );
					}
				}
			}
			this._elDragover = tar;
			this._itemDragover = elOver;
		}
	}
	_ondrop( e ) {
		this._droppedInside = this._elDragged && e.target.closest( this._parentSelector );
	}
	_ondragend() {
		if ( this._elDragged ) {
			const el = this._elDragged,
				oldInd = this._indDragged,
				oldPar = this._elDraggedParent;

			this._elDragged =
			this._elDragover =
			this._itemDragover = null;
			el.classList.remove( "gsuiReorder-dragging" );
			if ( this._elShadowDragged ) {
				this._elShadowDragged.classList.remove( "gsuiReorder-dragging" );
			}
			document.removeEventListener( "drop", this._ondrop );
			if ( this._droppedInside ) {
				const ind = this._getIndex( el );

				this._droppedInside = false;
				if ( ind !== oldInd || el.parentNode !== oldPar ) {
					this._onchange( el, oldInd, ind, oldPar, el.parentNode );
				}
			} else {
				el.remove();
				oldInd === 0
					? oldPar.prepend( el )
					: oldPar.children[ oldInd - 1 ].after( el );
			}
		}
	}
}
<|endoftext|>


<commit_before>var meta = {
    use: 'export',
    type: 'form',
    required: ['model', 'template']
};

/*
 Description: Converts the data and template to format that can be used to render a form.
 Filename:form.exporter.js
 Created Dated: 11/8/2013
 */

var module = function () {

    var log = new Log();

    /**
     * The function creates a options data object which will be used
     * to render drop downs
     * @param field  An object containing field information
     * @param fieldTemplate  : An object describing the structure of the field
     * @param data The options data will be added to this object
     */
    var buildOptionsObject = function (field, fieldTemplate, data) {
        //Only apply the logic if the field type is options
        if (!data.isOptions) {
            return;
        }

        var optionData = {};
        var options = csvToArray(fieldTemplate.value || '');
        optionData['selected'] = field.value;

        //Drop the selected value
        optionData['options'] = options.filter(function (element) {
            return element != field.value ? true : false;
        });

        data['optionData'] = optionData;

    };

    /**
     * The function is used to support unbounded tables
     * An unbounded table is treated as one single field containing
     * @param table
     * @param fieldArray
     * @param template   The template used to render an asset
     */
    var addUnboundTable = function (table, fieldArray, template) {
        var tableTemplate = template.getTable(table.name);
        var field;
        var fieldTemplate;
        var fieldToOutput;

        if (tableTemplate.maxoccurs == 'unbounded') {
            var data = {};
            data['isUnboundTable'] = true;
            data['name'] = table.name;
            data['fields'] = [];          //The fields contained with the unbounded table
            data['isRequired'] = false;
            data['isTextBox'] = false;
            data['isTextArea'] = false;
            data['isOptions'] = false;
            data['isOptionsText'] = false;
            data['isDate'] = false;

            data['isReadOnly'] = false;
            data['isEditable'] = true;
            data['isFile'] = false;
            data['columnLabels'] = [];
            data['columnNames'] = [];

            //Go through all the fields in the table
            for (var key in table.fields) {

                fieldToOutput = {};
                field = table.fields[key];
                fieldTemplate = template.getField(table.name, field.name);

                data.columnLabels.push(fieldTemplate.label);
                data.columnNames.push(fieldTemplate.name);

                fillFieldToOutput(fieldToOutput, field, fieldTemplate, table);

                data.fields.push(fieldToOutput);
            }

            create2DFields(data);

            fieldArray.push(data);

            return true;
        }

        return false;
    };

    /**
     * The function creates a 2d map of the fields so it can be rendered
     * easily in a tabular form
     * @param data
     */
    var create2DFields = function (data) {
        var fieldMap = [];
        var fieldRow;

        //var rows = data.fields[0].value.length;
        var rows = getFieldWithMostValues(data);

        //If there are no rows then create an empty row
        if(rows==0){
            createEmptyRow(data);
            return;
        }

        for (var index = 0; index < rows; index++) {

            fieldRow = [];

            //Populate a cloned field instance for each column of the row
            for (var fieldIndex in data.fields) {

                //var fieldValue = data.fields[fieldIndex].value[index];
                var fieldValue = getValueAtIndex(data.fields[fieldIndex], index);
                var clonedField = cloneField(data.fields[fieldIndex]);
                clonedField.value = fieldValue;
                //Give a unique name so the name can be used as an id
                clonedField.name = clonedField.name + '' + index;
                fieldRow.push(clonedField);
            }

            fieldMap.push(fieldRow);
        }

        data.fields = fieldMap;
    };

    var cloneField = function (field) {
        var clone = {};
        for (var key in field) {
            clone[key] = field[key];
        }

        return clone;
    };

    /**
     * The function locates the field with the most number of values
     * @param data
     * @returns The field which has the most number of values,else the first field
     */
    var getFieldWithMostValues = function (data) {
        var maxField = data.fields[0];
        var numValues;
        for (var index in data.fields) {

            //All fields will contain atleast one value (including the empty fields)
            numValues = getNumOfValues(data.fields[index]);

            if (getNumOfValues(maxField) < numValues) {
                maxField = data.fields[index];
            }
        }

        return getNumOfValues(maxField);
    };

    /**
     * The function inspects the value of a field to determine the number of invidual values it contains
     * @param field
     * @returns The number of values contained within the field
     */
    var getNumOfValues = function (field) {
        var count = 0;

        if (!field.value) {
            return count;
        }

        if (field.value instanceof Array) {
            count = field.value.length;
        }

        return count;
    };

    /**
     * The function safely obtains a value at a given index of a field value.If the field is not
     * an array then the immediate value is returned.If the field value is an array then the value
     * at the provided index is returned after a boundary check
     * @param field A field instance with a value
     * @param index The index of the value in the value array
     * @returns The value of the index at specified index if it is an array,else the immediate value of the field
     */
    var getValueAtIndex = function (field, index) {
        var result = ''; //We return an empty value by default

        //Check if the field value is an array
        if (field.value instanceof Array) {

            //Determine if the index is within the bounds of the array
            if (field.value.length >= index) {
                result = field.value[index];
            }
        }
        else {
            //Normal field
            result = field.value;
        }

        return result;
    };

    /**
     * The function creates an empty field row
     * @param data
     */
    var createEmptyRow = function (data) {
        var fieldRow = [];
        var fieldMap = [];

        for (var index in data.fields) {
            fieldRow.push(data.fields[index]);
        }

        fieldMap.push(fieldRow);

        data.fields = fieldMap;

    };

    /**
     * The function will create a composite data object based on a field template
     * and a field instance.
     * @param data  The object which will be the composite representation of the field and the field template
     * @param field The field instance to be inspected
     * @param fieldTemplate  The template of the field
     */
    var fillFieldToOutput = function (data, field, fieldTemplate, table) {
        data['name'] = table.name + '_' + field.name;
        data['label'] = (fieldTemplate.label) ? fieldTemplate.label : field.name;
        data['isRequired'] = (fieldTemplate.required) ? true : false;
        data['isTextBox'] = (fieldTemplate.type == 'text') ? true : false;
        data['isTextArea'] = (fieldTemplate.type == 'text-area') ? true : false;
        data['isOptions'] = (fieldTemplate.type == 'options') ? true : false;
        data['isOptionsText'] = (fieldTemplate.type == 'option-text') ? true : false;
        data['isDate'] = (fieldTemplate.type == 'date') ? true : false;

        data['isReadOnly'] = (fieldTemplate.meta.readOnly) ? fieldTemplate.meta.readOnly : false;
        data['isEditable'] = (fieldTemplate.meta.editable) ? fieldTemplate.meta.editable : false;
        data['isFile'] = (fieldTemplate.type == 'file') ? true : false;

        data['value'] = field.value;

        data['valueList'] = csvToArray(fieldTemplate.value || '');

        buildOptionsObject(field, fieldTemplate, data);
    }

    /*
     * Go through each table and extract field data
     */
    function fillFields(table, fieldArray, template) {

        //Handle unbounded table seperately
        if (addUnboundTable(table, fieldArray, template)) {
            return;
        }

        //var username=obtainUserNameFromSession();
        //log.debug('logged in user: '+username);
        //Go through each field
        for each(var field
    in
        table.fields
    )
        {

            //Obtain the field details from the template
            var fieldTemplate = template.getField(table.name, field.name);

            //We ignore the field if it is not in the template
            if (!fieldTemplate) {
                log.debug('Ignoring field: ' + stringify(fieldTemplate));
                return;
            }
            //Ignore the field which is set as hidden
            if (fieldTemplate.meta.hidden == "false") {
                var data = {};

                data['name'] = table.name + '_' + field.name;
                data['label'] = (fieldTemplate.label) ? fieldTemplate.label : field.name;
                data['isRequired'] = (fieldTemplate.required) ? true : false;
                data['isTextBox'] = (fieldTemplate.type == 'text') ? true : false;
                data['isTextArea'] = (fieldTemplate.type == 'text-area') ? true : false;
                data['isOptions'] = (fieldTemplate.type == 'options') ? true : false;
                data['isOptionsText'] = (fieldTemplate.type == 'option-text') ? true : false;
                data['isDate'] = (fieldTemplate.type == 'date') ? true : false;

                data['isReadOnly'] = (fieldTemplate.meta.readOnly) ? fieldTemplate.meta.readOnly : false;
                data['isEditable'] = (fieldTemplate.meta.editable) ? fieldTemplate.meta.editable : false;
                data['isFile'] = (fieldTemplate.type == 'file') ? true : false;

                data['value'] = field.value;

                data['valueList'] = csvToArray(fieldTemplate.value || '');

                buildOptionsObject(field, fieldTemplate, data);

                fieldArray.push(data);
            }

        }

        return fieldArray;
    }

    /*
     The function obtains the currently logged in user from the session
     */
    function obtainUserNameFromSession() {

        var username = 'unknown';
        try {
            username = require('store').server.current(session).username;//.get('LOGGED_IN_USER');
        }
        catch (e) {
            log.debug('Unable to retrieved logged in user from sessions.The following exception was thrown: ' + e);
        }
        return username;
    }

    /*
     * Fills all of the tables except the *(global)
     */
    function fillTables(model, template) {

        var fieldArray = [];

        //Go through each table in the model
        for each(var table
    in
        model.dataTables
    )
        {

            //Ignore if *
            if (table.name != '*') {
                fillFields(table, fieldArray, template);
            }
        }

        log.debug('Fields: ' + stringify(fieldArray));

        return fieldArray;
    }

    /*
     The function converts a string comma seperated value list to an array
     @str: A string representation of an array
     TODO: Move to utility script
     */
    function csvToArray(str) {
        var array = str.split(',');
        return array;
    }

    function fillMeta(model, template) {
        var meta = {};
        meta['shortName'] = template.shortName;
        meta['singularLabel'] = template.singularLabel;
        meta['pluralLabel'] = template.pluralLabel;

        log.debug('Meta: ' + stringify(meta));
        return meta;
    }

    function fillInfo(model) {
        var info = {};

        var field = model.getField('*.id');
        info['id'] = field ? field.getValue() : '';

        field = model.getField('*.lifecycle');
        info['lifecycle'] = field ? field.getValue() : '';

        field = model.getField('overview.version');
        info['version'] = field ? field.getValue() : '';

        field = model.getField('*.lifecycleState');

        info['lifecycleState'] = field ? field.getValue() : '';

        log.debug('Info: ' + stringify(info));

        return info;
    }

    return{
        execute: function (context) {

            log.debug('Entered: ' + meta.type);

            var model = context.model;
            var template = context.template;

            var struct = {};

            //TODO: Move this check outside
            if ((!model) || (!template)) {
                log.debug('Required parameters: ' + meta.required + 'not available to adapter');
                throw 'Required model and template data not present';
            }

            var tables = fillTables(model, template);

            struct['fields'] = tables;
            struct['meta'] = fillMeta(model, template);
            struct['info'] = fillInfo(model);

            log.debug('Leaving: ' + meta.type);

            return struct;

        }
    }
};
<commit_msg>COmments<commit_after>var meta = {
    use: 'export',
    type: 'form',
    required: ['model', 'template']
};

/*
 Description: Converts the data and template to format that can be used to render a form.
 Filename:form.exporter.js
 Created Dated: 11/8/2013
 */

var module = function () {

    var log = new Log();

    /**
     * The function creates a options data object which will be used
     * to render drop downs
     * @param field  An object containing field information
     * @param fieldTemplate  : An object describing the structure of the field
     * @param data The options data will be added to this object
     */
    var buildOptionsObject = function (field, fieldTemplate, data) {
        //Only apply the logic if the field type is options
        if (!data.isOptions) {
            return;
        }

        var optionData = {};
        var options = csvToArray(fieldTemplate.value || '');
        optionData['selected'] = field.value;

        //Drop the selected value
        optionData['options'] = options.filter(function (element) {
            return element != field.value ? true : false;
        });

        data['optionData'] = optionData;

    };

    /**
     * The function is used to support unbounded tables
     * An unbounded table is treated as one single field containing
     * @param table
     * @param fieldArray
     * @param template   The template used to render an asset
     */
    var addUnboundTable = function (table, fieldArray, template) {
        var tableTemplate = template.getTable(table.name);
        var field;
        var fieldTemplate;
        var fieldToOutput;

        if (tableTemplate.maxoccurs == 'unbounded') {
            var data = {};
            data['isUnboundTable'] = true;
            data['name'] = table.name;
            data['fields'] = [];          //The fields contained with the unbounded table
            data['isRequired'] = false;
            data['isTextBox'] = false;
            data['isTextArea'] = false;
            data['isOptions'] = false;
            data['isOptionsText'] = false;
            data['isDate'] = false;

            data['isReadOnly'] = false;
            data['isEditable'] = true;
            data['isFile'] = false;
            data['columnLabels'] = [];
            data['columnNames'] = [];

            //Go through all the fields in the table
            for (var key in table.fields) {

                fieldToOutput = {};
                field = table.fields[key];
                fieldTemplate = template.getField(table.name, field.name);

                data.columnLabels.push(fieldTemplate.label);
                data.columnNames.push(fieldTemplate.name);

                fillFieldToOutput(fieldToOutput, field, fieldTemplate, table);

                data.fields.push(fieldToOutput);
            }

            create2DFields(data);

            fieldArray.push(data);

            return true;
        }

        return false;
    };

    /**
     * The function creates a 2d map of the fields so it can be rendered
     * easily in a tabular form
     * @param data
     */
    var create2DFields = function (data) {
        var fieldMap = [];
        var fieldRow;

        //var rows = data.fields[0].value.length;
        var rows = getFieldWithMostValues(data);

        //If there are no rows then create an empty row
        if(rows==0){
            createEmptyRow(data);
            return;
        }

        for (var index = 0; index < rows; index++) {

            fieldRow = [];

            //Populate a cloned field instance for each column of the row
            for (var fieldIndex in data.fields) {

                //var fieldValue = data.fields[fieldIndex].value[index];
                var fieldValue = getValueAtIndex(data.fields[fieldIndex], index);
                var clonedField = cloneField(data.fields[fieldIndex]);
                clonedField.value = fieldValue;
                //Give a unique name so the name can be used as an id
                clonedField.name = clonedField.name + '' + index;
                fieldRow.push(clonedField);
            }

            fieldMap.push(fieldRow);
        }

        data.fields = fieldMap;
    };

    var cloneField = function (field) {
        var clone = {};
        for (var key in field) {
            clone[key] = field[key];
        }

        return clone;
    };

    /**
     * The function locates the field with the most number of values
     * @param data
     * @returns The field which has the most number of values,else the first field
     */
    var getFieldWithMostValues = function (data) {
        var maxField = data.fields[0];
        var numValues;
        for (var index in data.fields) {

            //All fields will contain atleast one value (including the empty fields)
            numValues = getNumOfValues(data.fields[index]);

            if (getNumOfValues(maxField) < numValues) {
                maxField = data.fields[index];
            }
        }

        return getNumOfValues(maxField);
    };

    /**
     * The function inspects the value of a field to determine the number of individual values it contains
     * @param field
     * @returns The number of values contained within the field
     */
    var getNumOfValues = function (field) {
        var count = 0;

        if (!field.value) {
            return count;
        }

        if (field.value instanceof Array) {
            count = field.value.length;
        }

        return count;
    };

    /**
     * The function safely obtains a value at a given index of a field value.If the field is not
     * an array then the immediate value is returned.If the field value is an array then the value
     * at the provided index is returned after a boundary check
     * @param field A field instance with a value
     * @param index The index of the value in the value array
     * @returns The value of the index at specified index if it is an array,else the immediate value of the field
     */
    var getValueAtIndex = function (field, index) {
        var result = ''; //We return an empty value by default

        //Check if the field value is an array
        if (field.value instanceof Array) {

            //Determine if the index is within the bounds of the array
            if (field.value.length >= index) {
                result = field.value[index];
            }
        }
        else {
            //Normal field
            result = field.value;
        }

        return result;
    };

    /**
     * The function creates an empty field row
     * @param data
     */
    var createEmptyRow = function (data) {
        var fieldRow = [];
        var fieldMap = [];

        for (var index in data.fields) {
            fieldRow.push(data.fields[index]);
        }

        fieldMap.push(fieldRow);

        data.fields = fieldMap;

    };

    /**
     * The function will create a composite data object based on a field template
     * and a field instance.
     * @param data  The object which will be the composite representation of the field and the field template
     * @param field The field instance to be inspected
     * @param fieldTemplate  The template of the field
     */
    var fillFieldToOutput = function (data, field, fieldTemplate, table) {
        data['name'] = table.name + '_' + field.name;
        data['label'] = (fieldTemplate.label) ? fieldTemplate.label : field.name;
        data['isRequired'] = (fieldTemplate.required) ? true : false;
        data['isTextBox'] = (fieldTemplate.type == 'text') ? true : false;
        data['isTextArea'] = (fieldTemplate.type == 'text-area') ? true : false;
        data['isOptions'] = (fieldTemplate.type == 'options') ? true : false;
        data['isOptionsText'] = (fieldTemplate.type == 'option-text') ? true : false;
        data['isDate'] = (fieldTemplate.type == 'date') ? true : false;

        data['isReadOnly'] = (fieldTemplate.meta.readOnly) ? fieldTemplate.meta.readOnly : false;
        data['isEditable'] = (fieldTemplate.meta.editable) ? fieldTemplate.meta.editable : false;
        data['isFile'] = (fieldTemplate.type == 'file') ? true : false;

        data['value'] = field.value;

        data['valueList'] = csvToArray(fieldTemplate.value || '');

        buildOptionsObject(field, fieldTemplate, data);
    }

    /*
     * Go through each table and extract field data
     */
    function fillFields(table, fieldArray, template) {

        //Handle unbounded table seperately
        if (addUnboundTable(table, fieldArray, template)) {
            return;
        }

        //var username=obtainUserNameFromSession();
        //log.debug('logged in user: '+username);
        //Go through each field
        for each(var field
    in
        table.fields
    )
        {

            //Obtain the field details from the template
            var fieldTemplate = template.getField(table.name, field.name);

            //We ignore the field if it is not in the template
            if (!fieldTemplate) {
                log.debug('Ignoring field: ' + stringify(fieldTemplate));
                return;
            }
            //Ignore the field which is set as hidden
            if (fieldTemplate.meta.hidden == "false") {
                var data = {};

                data['name'] = table.name + '_' + field.name;
                data['label'] = (fieldTemplate.label) ? fieldTemplate.label : field.name;
                data['isRequired'] = (fieldTemplate.required) ? true : false;
                data['isTextBox'] = (fieldTemplate.type == 'text') ? true : false;
                data['isTextArea'] = (fieldTemplate.type == 'text-area') ? true : false;
                data['isOptions'] = (fieldTemplate.type == 'options') ? true : false;
                data['isOptionsText'] = (fieldTemplate.type == 'option-text') ? true : false;
                data['isDate'] = (fieldTemplate.type == 'date') ? true : false;

                data['isReadOnly'] = (fieldTemplate.meta.readOnly) ? fieldTemplate.meta.readOnly : false;
                data['isEditable'] = (fieldTemplate.meta.editable) ? fieldTemplate.meta.editable : false;
                data['isFile'] = (fieldTemplate.type == 'file') ? true : false;

                data['value'] = field.value;

                data['valueList'] = csvToArray(fieldTemplate.value || '');

                buildOptionsObject(field, fieldTemplate, data);

                fieldArray.push(data);
            }

        }

        return fieldArray;
    }

    /*
     The function obtains the currently logged in user from the session
     */
    function obtainUserNameFromSession() {

        var username = 'unknown';
        try {
            username = require('store').server.current(session).username;//.get('LOGGED_IN_USER');
        }
        catch (e) {
            log.debug('Unable to retrieved logged in user from sessions.The following exception was thrown: ' + e);
        }
        return username;
    }

    /*
     * Fills all of the tables except the *(global)
     */
    function fillTables(model, template) {

        var fieldArray = [];

        //Go through each table in the model
        for each(var table
    in
        model.dataTables
    )
        {

            //Ignore if *
            if (table.name != '*') {
                fillFields(table, fieldArray, template);
            }
        }

        log.debug('Fields: ' + stringify(fieldArray));

        return fieldArray;
    }

    /*
     The function converts a string comma seperated value list to an array
     @str: A string representation of an array
     TODO: Move to utility script
     */
    function csvToArray(str) {
        var array = str.split(',');
        return array;
    }

    function fillMeta(model, template) {
        var meta = {};
        meta['shortName'] = template.shortName;
        meta['singularLabel'] = template.singularLabel;
        meta['pluralLabel'] = template.pluralLabel;

        log.debug('Meta: ' + stringify(meta));
        return meta;
    }

    function fillInfo(model) {
        var info = {};

        var field = model.getField('*.id');
        info['id'] = field ? field.getValue() : '';

        field = model.getField('*.lifecycle');
        info['lifecycle'] = field ? field.getValue() : '';

        field = model.getField('overview.version');
        info['version'] = field ? field.getValue() : '';

        field = model.getField('*.lifecycleState');

        info['lifecycleState'] = field ? field.getValue() : '';

        log.debug('Info: ' + stringify(info));

        return info;
    }

    return{
        execute: function (context) {

            log.debug('Entered: ' + meta.type);

            var model = context.model;
            var template = context.template;

            var struct = {};

            //TODO: Move this check outside
            if ((!model) || (!template)) {
                log.debug('Required parameters: ' + meta.required + 'not available to adapter');
                throw 'Required model and template data not present';
            }

            var tables = fillTables(model, template);

            struct['fields'] = tables;
            struct['meta'] = fillMeta(model, template);
            struct['info'] = fillInfo(model);

            log.debug('Leaving: ' + meta.type);

            return struct;

        }
    }
};
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
var format = function(data){
	var unixtime, newDate,
		newDate = new Date(),
		artifacts = data.artifacts;
	for(var i in artifacts){
		unixtime = artifacts[i].attributes['overview_createdtime'];
		newDate.setTime(unixtime);
		data.artifacts[i].attributes['overview_createdtime'] = newDate.toUTCString();
	}
	return data;
};

var resources = function(){
	return {
		js:['select2.min.js','tags/tags-common.js','tags/tags-init-view-asset.js','view-asset.js','taxonomy-browser.js','taxonomy-view-asset.js'],
		css:['select2.min.css','taxonomy-browser.css']
	}
};<commit_msg>STORE-1392 fix<commit_after>/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
var format = function(data){
	var unixtime, newDate,
		newDate = new Date(),
		artifacts = data.artifacts;
	for(var i in artifacts){
		unixtime = artifacts[i].attributes['overview_createdtime'];
		newDate.setTime(unixtime);
		data.artifacts[i].attributes['overview_createdtime'] = newDate.toUTCString();
	}
	return data;
};

var resources = function(){
    return {
        js: ['select2.min.js', 'tags/tags-common.js', 'tags/tags-init-view-asset.js', 'view-asset.js', 'taxonomy-browser.js', 'taxonomy-view-asset.js'],
        css: ['select2.min.css', 'taxonomy-browser.css'],
        code: ['publisher.assets.hbs']
    }
};<|endoftext|>