<commit_before>import $ from 'jquery'
import Util from './util'

/* eslint no-magic-numbers: ["error", { "ignore": [1,2] }] */

/**
 * --------------------------------------------------------------------------
 * Boosted (v4.0.0): o-megamenu.js
 * Licensed under MIT (https://github.com/Orange-OpenSource/Orange-Boosted-Bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

const MegaMenu = (() => {
  /**
   * ------------------------------------------------------------------------
   * Constants
   * ------------------------------------------------------------------------
   */

  const NAME = 'megamenu'
  const VERSION = '4.0.0'
  const DATA_KEY = 'bs.megamenu'
  const JQUERY_NO_CONFLICT = $.fn[NAME]
  const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
  const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
  const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
  const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
  const TIMEOUT = 1000 // Timeout befor focusing first element
  const PERCENTAGE = 100 // Width slide proportion
  const SPLITLENGHT = 4

  const ClassName = {
    TRANSITIONING: 'transitioning'
  }

  const Selector = {
    MEGAMENU    : '.mega-menu',
    ROOT_NAV : '.mega-menu > .navbar-nav',
    MEGAMENU_PANEL : '.mega-menu-panel',
    MEGAMENU_NAV : '.nav-link + .navbar-nav',
    NAV_MENU : '.navbar-nav',
    NAV_ITEM : '.nav-item',
    NAV_LINK : '.nav-link',
    NAV_LINK_COLLAPSE : '.nav-link[data-toggle=collapse]',
    NAV_LINK_BACK : '.nav-link.back',
    NAV_LINK_EXPANDED : '.nav-link[aria-expanded=true]'
  }

  /**
   * ------------------------------------------------------------------------
   * Class Definition
   * ------------------------------------------------------------------------
   */

  class MegaMenu {
    constructor(element, config) {
      this._element = element
      this._config = config
      this._$navLinks = $(this._element).find(Selector.NAV_LINK)
      this._$goForwardLinks = $(this._element).find(Selector.MEGAMENU_NAV).prev(Selector.NAV_LINK)
      this._$goBackLinks = $(this._element).find(Selector.NAV_LINK_BACK)
      this._$topCollapseMenus = $(this._element).find(Selector.MEGAMENU_PANEL)
      this._$navLinkCollapses = $(this._element).find(Selector.NAV_LINK_COLLAPSE)
      this._addEventListeners()
      this._addAriaAttributes(this._element)
      this.goTo = this._initPosition
    }

    // getters

    static get VERSION() {
      return VERSION
    }

    // public

    // private

    _addEventListeners() {
      this._$goForwardLinks.on('click', (event) => this._goForward(event))
      this._$goBackLinks.on('click', (event) => this._goBackward(event))
      this._$navLinks.on('keydown', (event) => this._manageKeyDown(event))
      this._$topCollapseMenus.on('shown.bs.collapse', this._collapseFocus)
      this._$navLinkCollapses.on('click', (event) => this._handleCollapseToggle(event))
    }

    _addAriaAttributes(element) {
      const $subNavs = $(element).find('.nav-link + .navbar-nav')

      $(element).attr('role', 'application')
      $(element).find('> .navbar-nav').attr('role', 'menu')
      $(element).find(Selector.MEGAMENU_PANEL).attr('role', 'menu')
      $(element).find('.nav-link[data-toggle=collapse]').attr('role', 'menuitem')
      $(element).find(Selector.NAV_LINK_BACK).attr({
        'aria-hidden': true
      })
      $(element).find(Selector.NAV_ITEM).attr('role', 'presentation')

      $subNavs.each(function () {
        const navId = Util.getUID(NAME)
        const $thisNavToggler = $(this).prev(Selector.NAV_LINK)
        const $thisNav = $(this)
        const $thisNavBackLink = $thisNav.find(Selector.NAV_LINK_BACK)
        const $topMenu = $(this).closest(Selector.NAV_MENU).parent().closest(Selector.NAV_MENU).prev(Selector.NAV_LINK)
        let goBackLabel = `go back to ${$topMenu.text()} menu`

        if (!$topMenu.length) {
          goBackLabel = `go back to ${$(this).closest(Selector.MEGAMENU_PANEL).prev(Selector.NAV_LINK).text()} menu`
        }

        $thisNav.attr({
          id: navId,
          role: 'menu'
        })
        $thisNavToggler.attr({
          role: 'menuitem',
          'aria-controls': navId,
          'aria-expanded': false,
          'aria-haspopup': true
        })
        $thisNavBackLink.attr({
          role: 'menuitem',
          'aria-controls': navId,
          'aria-label': goBackLabel
        })
      })
    }

    _initPosition(target) {
      if (!$(target).length) {
        return
      }

      const $target = $(target).first()
      const position = $target.parents().index(this._element)
      const rootPosition = $('.mega-menu-panel .nav-link').first().parents().index($('.mega-menu'))
      const translatePercentage = -(position - rootPosition) * PERCENTAGE / 2
      const $thisNav = $target.closest(Selector.NAV_MENU)
      const $rootNav = $(Selector.ROOT_NAV)

      $rootNav.addClass(ClassName.TRANSITIONING)

      // open collapse
      if ($target.attr('data-toggle') === 'collapse') {
        $target.siblings(Selector.MEGAMENU_PANEL).collapse('show')
        this._$topCollapseMenus.not($target.siblings(Selector.MEGAMENU_PANEL)).collapse('hide')
        $(this._element).height('auto')
        $rootNav.css('transform', 'translateX(0%)')
      } else {
        $target.closest(Selector.MEGAMENU_PANEL).collapse('show')
        this._$topCollapseMenus.not($target.closest(Selector.MEGAMENU_PANEL)).collapse('hide')

        // show menu and hide other
        $target.parents(Selector.NAV_MENU).show()

        // set aria on parent links
        $target.parents(Selector.NAV_ITEM).find('> .nav-link').not($target).attr({
          tabindex: -1,
          'aria-hidden': true,
          'aria-expanded': true
        })

        // translate to pos
        $rootNav.css('transform', `translateX(${translatePercentage}%)`)
        if (translatePercentage) {
          // adapt main collapse height to target height
          $(this._element).height($thisNav.height())
        } else {
          $(this._element).height('auto')
        }
      }

      // set focus on target link
      setTimeout(() =>  {
        // set focus on target link
        $target.trigger('focus')

        $rootNav.removeClass(ClassName.TRANSITIONING)
      }, TIMEOUT)
    }

    _manageKeyDown(event) {
      const $thisTarget = $(event.target)

      // test key code
      if (/input|textarea/i.test(event.target.tagName)) {
        return
      }

      // proceed according to key code
      switch (event.which) {
        case ARROW_LEFT_KEYCODE:
          this._goBackward(event)
          break
        case ARROW_RIGHT_KEYCODE:
          this._goForward(event)
          break
        case ARROW_UP_KEYCODE:
          // focus prev nav link
          $thisTarget.parent().prev().find('>.nav-link').not(Selector.NAV_LINK_BACK).trigger('focus')
          break
        case ARROW_DOWN_KEYCODE:
          // focus next nav link
          $thisTarget.parent().next().find('>.nav-link').trigger('focus')
          break
        default:
      }
    }

    _collapseFocus() {
      $(this).find(Selector.NAV_LINK).not(Selector.NAV_LINK_BACK).first().trigger('focus')
    }

    _handleCollapseToggle(e) {
      const $this = $(e.target)
      const $thisCollapse = $($this.attr('href'))

      this._$topCollapseMenus.not($thisCollapse).collapse('hide')
    }

    _goForward(e) {
      e.preventDefault()
      const $this = $(e.target)
      const $thisNav = $this.closest(Selector.NAV_MENU)
      const $targetNav =  $this.next(Selector.NAV_MENU)
      const $rootNav = $(Selector.ROOT_NAV)
      const $thisNavToggler = $this
      const currentTranslatePos = parseInt($rootNav.css('transform').split(',')[SPLITLENGHT], 10)
      const navWidth = $rootNav.width()
      const currentTranslatePercentage = PERCENTAGE * currentTranslatePos / navWidth

      if (!$this.next(Selector.NAV_MENU).length || $rootNav.hasClass(ClassName.TRANSITIONING)) {
        return false
      }

      $rootNav.addClass(ClassName.TRANSITIONING)

      // hide all nav on same level
      $thisNav.find(Selector.NAV_MENU).hide()

      // show target navbar-nav
      $targetNav.show()

      // adapt main collapse height to target height
      $(Selector.MEGAMENU).height($targetNav.height())

      // make only visible elements focusable
      if (!currentTranslatePercentage) {
        $rootNav.find('>.nav-item .nav-link').attr({
          tabindex: -1,
          'aria-hidden': true
        })
      }
      $thisNav.find(Selector.NAV_LINK).attr({
        tabindex: -1,
        'aria-hidden': true
      })
      $targetNav.find(Selector.NAV_LINK).attr({
        tabindex: 0,
        'aria-hidden': false
      })

      // translate menu
      $rootNav.css('transform', `translateX(${currentTranslatePercentage - PERCENTAGE}%)`)

      // focus on target nav first item
      $rootNav.one('transitionend', () => {
        $thisNavToggler.attr('aria-expanded', true)
        $targetNav.find(Selector.NAV_LINK).not(Selector.NAV_LINK_BACK).first().trigger('focus')
        $rootNav.removeClass(ClassName.TRANSITIONING)
      })
      return true
    }

    _goBackward(e) {
      e.preventDefault()

      const $this = $(e.target)
      const $thisNav = $this.closest(Selector.NAV_MENU)
      const $targetNav = $thisNav.parent().closest(Selector.NAV_MENU)
      const $rootNav = $(Selector.ROOT_NAV)
      const $targetNavToggler = $targetNav.find(Selector.NAV_LINK_EXPANDED)
      const currentTranslatePos = parseInt($rootNav.css('transform').split(',')[SPLITLENGHT], 10)
      const navWidth = $rootNav.width()
      const currentTranslatePercentage = PERCENTAGE * currentTranslatePos / navWidth

      if (!currentTranslatePercentage || $rootNav.hasClass(ClassName.TRANSITIONING)) {
        return false
      }

      $rootNav.addClass(ClassName.TRANSITIONING)

      // make only visible elements focusable
      $targetNav.find(Selector.NAV_LINK).attr({
        tabindex: 0,
        'aria-hidden': false
      })
      if (currentTranslatePercentage === -PERCENTAGE) {
        // reset main collapse height
        $(Selector.MEGAMENU).css('height', 'auto')
        $rootNav.find('>.nav-item .nav-link').attr({
          tabindex: 0,
          'aria-hidden': false
        })
      }

      // translate menu
      $rootNav.css('transform', `translateX(${currentTranslatePercentage + PERCENTAGE}%)`)

      // focus on target nav first item
      $rootNav.one('transitionend', () => {
        $targetNavToggler.attr('aria-expanded', false)
        $targetNavToggler.trigger('focus')
        $thisNav.hide()
        $rootNav.removeClass(ClassName.TRANSITIONING)
      })
      return true
    }

    // static

    static _jQueryInterface(config) {
      return this.each(function () {
        const $element = $(this)

        if (!$element.is(Selector.MEGAMENU)) {
          throw new Error('Element is not a mega menu')
        }

        let data       = $element.data(DATA_KEY)

        if (!data) {
          data = new MegaMenu(this, config)
          $element.data(DATA_KEY, data)
        }

        if (typeof config !== 'undefined' && config) {
          if (typeof config !== 'string' || !/^[.#].*/.test(config)) {
            throw new Error(`Selector "${config}" is not supported`)
          }

          data.goTo(config)
        }
      })
    }
  }

  /**
   * ------------------------------------------------------------------------
   * jQuery
   * ------------------------------------------------------------------------
   */

  $.fn[NAME]             = MegaMenu._jQueryInterface
  $.fn[NAME].Constructor = MegaMenu
  $.fn[NAME].noConflict  = function () {
    $.fn[NAME] = JQUERY_NO_CONFLICT
    return MegaMenu._jQueryInterface
  }

  return MegaMenu
})($)

export default MegaMenu
<commit_msg>feat(component): disable focus on init<commit_after>import $ from 'jquery'
import Util from './util'

/* eslint no-magic-numbers: ["error", { "ignore": [1,2] }] */

/**
 * --------------------------------------------------------------------------
 * Boosted (v4.0.0): o-megamenu.js
 * Licensed under MIT (https://github.com/Orange-OpenSource/Orange-Boosted-Bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

const MegaMenu = (() => {
  /**
   * ------------------------------------------------------------------------
   * Constants
   * ------------------------------------------------------------------------
   */

  const NAME = 'megamenu'
  const VERSION = '4.0.0'
  const DATA_KEY = 'bs.megamenu'
  const JQUERY_NO_CONFLICT = $.fn[NAME]
  const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
  const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
  const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
  const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
  const TIMEOUT = 1000 // Timeout befor focusing first element
  const PERCENTAGE = 100 // Width slide proportion
  const SPLITLENGHT = 4

  const ClassName = {
    TRANSITIONING: 'transitioning'
  }

  const Selector = {
    MEGAMENU    : '.mega-menu',
    ROOT_NAV : '.mega-menu > .navbar-nav',
    MEGAMENU_PANEL : '.mega-menu-panel',
    MEGAMENU_NAV : '.nav-link + .navbar-nav',
    NAV_MENU : '.navbar-nav',
    NAV_ITEM : '.nav-item',
    NAV_LINK : '.nav-link',
    NAV_LINK_COLLAPSE : '.nav-link[data-toggle=collapse]',
    NAV_LINK_BACK : '.nav-link.back',
    NAV_LINK_EXPANDED : '.nav-link[aria-expanded=true]'
  }

  /**
   * ------------------------------------------------------------------------
   * Class Definition
   * ------------------------------------------------------------------------
   */

  class MegaMenu {
    constructor(element, config) {
      this._element = element
      this._$navLinks = $(this._element).find(Selector.NAV_LINK)
      this._$goForwardLinks = $(this._element).find(Selector.MEGAMENU_NAV).prev(Selector.NAV_LINK)
      this._$goBackLinks = $(this._element).find(Selector.NAV_LINK_BACK)
      this._$topCollapseMenus = $(this._element).find(Selector.MEGAMENU_PANEL)
      this._$navLinkCollapses = $(this._element).find(Selector.NAV_LINK_COLLAPSE)
      this._config = config
      if (typeof this._config.noFocus === 'undefined') {
        this._config.noFocus = false
      }
      this._addEventListeners()
      this._addAriaAttributes(this._element)
      this.goTo = this._initPosition
    }

    // getters

    static get VERSION() {
      return VERSION
    }

    // public

    // private

    _addEventListeners() {
      this._$goForwardLinks.on('click', (event) => this._goForward(event))
      this._$goBackLinks.on('click', (event) => this._goBackward(event))
      this._$navLinks.on('keydown', (event) => this._manageKeyDown(event))
      if (!this._config.noFocus) {
        this._$topCollapseMenus.on('shown.bs.collapse', this._collapseFocus)
      }
      this._$navLinkCollapses.on('click', (event) => this._handleCollapseToggle(event))
    }

    _addAriaAttributes(element) {
      const $subNavs = $(element).find('.nav-link + .navbar-nav')

      $(element).attr('role', 'application')
      $(element).find('> .navbar-nav').attr('role', 'menu')
      $(element).find(Selector.MEGAMENU_PANEL).attr('role', 'menu')
      $(element).find('.nav-link[data-toggle=collapse]').attr('role', 'menuitem')
      $(element).find(Selector.NAV_LINK_BACK).attr({
        'aria-hidden': true
      })
      $(element).find(Selector.NAV_ITEM).attr('role', 'presentation')

      $subNavs.each(function () {
        const navId = Util.getUID(NAME)
        const $thisNavToggler = $(this).prev(Selector.NAV_LINK)
        const $thisNav = $(this)
        const $thisNavBackLink = $thisNav.find(Selector.NAV_LINK_BACK)
        const $topMenu = $(this).closest(Selector.NAV_MENU).parent().closest(Selector.NAV_MENU).prev(Selector.NAV_LINK)
        let goBackLabel = `go back to ${$topMenu.text()} menu`

        if (!$topMenu.length) {
          goBackLabel = `go back to ${$(this).closest(Selector.MEGAMENU_PANEL).prev(Selector.NAV_LINK).text()} menu`
        }

        $thisNav.attr({
          id: navId,
          role: 'menu'
        })
        $thisNavToggler.attr({
          role: 'menuitem',
          'aria-controls': navId,
          'aria-expanded': false,
          'aria-haspopup': true
        })
        $thisNavBackLink.attr({
          role: 'menuitem',
          'aria-controls': navId,
          'aria-label': goBackLabel
        })
      })
    }

    _initPosition(target) {
      if (!$(target).length) {
        return
      }

      const $target = $(target).first()
      const position = $target.parents().index(this._element)
      const rootPosition = $('.mega-menu-panel .nav-link').first().parents().index($('.mega-menu'))
      const translatePercentage = -(position - rootPosition) * PERCENTAGE / 2
      const $thisNav = $target.closest(Selector.NAV_MENU)
      const $rootNav = $(Selector.ROOT_NAV)

      $rootNav.addClass(ClassName.TRANSITIONING)

      // open collapse
      if ($target.attr('data-toggle') === 'collapse') {
        $target.siblings(Selector.MEGAMENU_PANEL).collapse('show')
        this._$topCollapseMenus.not($target.siblings(Selector.MEGAMENU_PANEL)).collapse('hide')
        $(this._element).height('auto')
        $rootNav.css('transform', 'translateX(0%)')
      } else {
        $target.closest(Selector.MEGAMENU_PANEL).collapse('show')
        this._$topCollapseMenus.not($target.closest(Selector.MEGAMENU_PANEL)).collapse('hide')

        // show menu and hide other
        $target.parents(Selector.NAV_MENU).show()

        // set aria on parent links
        $target.parents(Selector.NAV_ITEM).find('> .nav-link').not($target).attr({
          tabindex: -1,
          'aria-hidden': true,
          'aria-expanded': true
        })

        // translate to pos
        $rootNav.css('transform', `translateX(${translatePercentage}%)`)
        if (translatePercentage) {
          // adapt main collapse height to target height
          $(this._element).height($thisNav.height())
        } else {
          $(this._element).height('auto')
        }
      }

      // set focus on target link
      setTimeout(() =>  {
        if (!this._config.noFocus) {
          // set focus on target link
          $target.trigger('focus')
        }

        $rootNav.removeClass(ClassName.TRANSITIONING)
      }, TIMEOUT)
    }

    _manageKeyDown(event) {
      const $thisTarget = $(event.target)

      // test key code
      if (/input|textarea/i.test(event.target.tagName)) {
        return
      }

      // proceed according to key code
      switch (event.which) {
        case ARROW_LEFT_KEYCODE:
          this._goBackward(event)
          break
        case ARROW_RIGHT_KEYCODE:
          this._goForward(event)
          break
        case ARROW_UP_KEYCODE:
          // focus prev nav link
          $thisTarget.parent().prev().find('>.nav-link').not(Selector.NAV_LINK_BACK).trigger('focus')
          break
        case ARROW_DOWN_KEYCODE:
          // focus next nav link
          $thisTarget.parent().next().find('>.nav-link').trigger('focus')
          break
        default:
      }
    }

    _collapseFocus() {
      $(this).find(Selector.NAV_LINK).not(Selector.NAV_LINK_BACK).first().trigger('focus')
    }

    _handleCollapseToggle(e) {
      const $this = $(e.target)
      const $thisCollapse = $($this.attr('href'))

      this._$topCollapseMenus.not($thisCollapse).collapse('hide')
    }

    _goForward(e) {
      e.preventDefault()
      const $this = $(e.target)
      const $thisNav = $this.closest(Selector.NAV_MENU)
      const $targetNav =  $this.next(Selector.NAV_MENU)
      const $rootNav = $(Selector.ROOT_NAV)
      const $thisNavToggler = $this
      const currentTranslatePos = parseInt($rootNav.css('transform').split(',')[SPLITLENGHT], 10)
      const navWidth = $rootNav.width()
      const currentTranslatePercentage = PERCENTAGE * currentTranslatePos / navWidth

      if (!$this.next(Selector.NAV_MENU).length || $rootNav.hasClass(ClassName.TRANSITIONING)) {
        return false
      }

      $rootNav.addClass(ClassName.TRANSITIONING)

      // hide all nav on same level
      $thisNav.find(Selector.NAV_MENU).hide()

      // show target navbar-nav
      $targetNav.show()

      // adapt main collapse height to target height
      $(Selector.MEGAMENU).height($targetNav.height())

      // make only visible elements focusable
      if (!currentTranslatePercentage) {
        $rootNav.find('>.nav-item .nav-link').attr({
          tabindex: -1,
          'aria-hidden': true
        })
      }
      $thisNav.find(Selector.NAV_LINK).attr({
        tabindex: -1,
        'aria-hidden': true
      })
      $targetNav.find(Selector.NAV_LINK).attr({
        tabindex: 0,
        'aria-hidden': false
      })

      // translate menu
      $rootNav.css('transform', `translateX(${currentTranslatePercentage - PERCENTAGE}%)`)

      // focus on target nav first item
      $rootNav.one('transitionend', () => {
        $thisNavToggler.attr('aria-expanded', true)
        $targetNav.find(Selector.NAV_LINK).not(Selector.NAV_LINK_BACK).first().trigger('focus')
        $rootNav.removeClass(ClassName.TRANSITIONING)
      })
      return true
    }

    _goBackward(e) {
      e.preventDefault()

      const $this = $(e.target)
      const $thisNav = $this.closest(Selector.NAV_MENU)
      const $targetNav = $thisNav.parent().closest(Selector.NAV_MENU)
      const $rootNav = $(Selector.ROOT_NAV)
      const $targetNavToggler = $targetNav.find(Selector.NAV_LINK_EXPANDED)
      const currentTranslatePos = parseInt($rootNav.css('transform').split(',')[SPLITLENGHT], 10)
      const navWidth = $rootNav.width()
      const currentTranslatePercentage = PERCENTAGE * currentTranslatePos / navWidth

      if (!currentTranslatePercentage || $rootNav.hasClass(ClassName.TRANSITIONING)) {
        return false
      }

      $rootNav.addClass(ClassName.TRANSITIONING)

      // make only visible elements focusable
      $targetNav.find(Selector.NAV_LINK).attr({
        tabindex: 0,
        'aria-hidden': false
      })
      if (currentTranslatePercentage === -PERCENTAGE) {
        // reset main collapse height
        $(Selector.MEGAMENU).css('height', 'auto')
        $rootNav.find('>.nav-item .nav-link').attr({
          tabindex: 0,
          'aria-hidden': false
        })
      }

      // translate menu
      $rootNav.css('transform', `translateX(${currentTranslatePercentage + PERCENTAGE}%)`)

      // focus on target nav first item
      $rootNav.one('transitionend', () => {
        $targetNavToggler.attr('aria-expanded', false)
        $targetNavToggler.trigger('focus')
        $thisNav.hide()
        $rootNav.removeClass(ClassName.TRANSITIONING)
      })
      return true
    }

    // static

    static _jQueryInterface(config) {
      return this.each(function () {
        const $element = $(this)

        if (!$element.is(Selector.MEGAMENU)) {
          throw new Error('Element is not a mega menu')
        }

        if (!config) {
          config = {}
        } else if (config.noFocus && typeof config.noFocus !== 'boolean') {
          // param = true
          throw new Error('disable-focus parameter must be boolean')
        }

        let data       = $element.data(DATA_KEY)

        if (!data) {
          data = new MegaMenu(this, config)
          $element.data(DATA_KEY, data)
        }

        if (config.target) {
          if (typeof config.target !== 'string' || !/^[.#].*/.test(config.target)) {
            throw new Error(`Selector "${config.target}" is not supported`)
          }

          data.goTo(config.target)
        }
      })
    }
  }

  /**
   * ------------------------------------------------------------------------
   * jQuery
   * ------------------------------------------------------------------------
   */

  $.fn[NAME]             = MegaMenu._jQueryInterface
  $.fn[NAME].Constructor = MegaMenu
  $.fn[NAME].noConflict  = function () {
    $.fn[NAME] = JQUERY_NO_CONFLICT
    return MegaMenu._jQueryInterface
  }

  return MegaMenu
})($)

export default MegaMenu
<|endoftext|>


<commit_before>module.exports = function(JSONObject) {
		// get our request information in string format
	var action = JSON.stringify(JSONObject.result.metadata.action)
	var spokenResult = JSON.stringify(JSONObject.result.metadata.speech)
	var parameters = JSON.stringify(JSONObject.result.metadata.parameters)
	var request = JSON.stringify(JSONObject.result.resolvedQuery)

		// initialize our universal principle responses
	var killingRes = ['Sorry, but I am programmed to know better. Killing is wrong in any context.', 'Dont you know that murder is abhorrent?', 'Sorry, murder is always immoral.']
	var harmingRes = ['The golden rule forbids it.', 'Did you not know that harming others is wrong?', 'Sorry, suffering is wrong, and if one can, they ought to prevent it.']
	var adulteryRes = ['Uh oh, adultery is not perimissible.', 'Sorry, lust and adultery are against my moral principles.', 'I am programmed to know better, as lustful behavior is wrong.']
	var deceptionRes = ['Deception is universally forbidden.', 'Nobody likes a liar.', 'Sorry, deception should never be allowed.']

		// initialize our word bank of forbidden universals
	var killing = ["kill", "murder", "assassinate", "destroy", "lynch", "slaughter", "slay", "annihilate"]
	var harming = ["shoot","butcher","massacre","rape","assualt","stab","punch","kick","brutalize", "injur", "assail", "jab","abuse","beat","harm","kidnap","torture"]
	var adultery = ["hooker", "prostitute", "rape", "brothel", "bimbo", "broad", "nympho", "tramp", "whore", "slut"]
	var deception = ["lie", "cheat", "decieve", "trick", "fake", "steal", "rob"]

		//initialize our word bank for types of action
	var badActs = ["smalltalk", "input.unknown", "messages.write", "shopping", "facebook","wisdom.person"]
	var neutralActs = ["wisdom"]
	var goodActs = ["apps", "auth", "calculator", "device", "finance"]

		// initialize our flags
	var kill = false;
	var harm = false;
	var adulter = false;
	var decieve = false;
	var questionableAct = false;
	var goodAct = false;
	var neutralAct = false;

		// check our type of action for potential wrong doing
	if(action){
		badActs.forEach(function(word){
			if(action.indexOf(word) > -1)
				questionableAct = true
		})
		goodActs.forEach(function(word){
			if(action.indexOf(word) > -1)
				goodAct = true
		})
		neutralActs.forEach(function(word){
			if(action.indexOf(word) > -1)
				neutralAct = true
		})
	}
	else
		questionableAct = true

		// check the requested act for forbidden universals
	if(request){
		killing.forEach(function(word){
			if(request.indexOf(word) > -1)
				kill = true
		})
		harming.forEach(function(word){
			if(request.indexOf(word) > -1)
				harm = true
		})
		adultery.forEach(function(word){
			if(request.indexOf(word) > -1)
				adulter = true
		})
		deception.forEach(function(word){
			if(request.indexOf(word) > -1)
				decieve = true
		})
	}

		// if the user is looking to do something which is morally permissible, return 0
	if(goodAct)
		return "0"
	if(neutralAct)
		return "1"
		// if the user is engaging in something questionably permissible, check for forbidden universals
	if(questionableAct){
		if(kill)
			return killingRes[Math.floor(Math.random() * 3)];
		else if(harm)
			return harmingRes[Math.floor(Math.random() * 3)];
		else if(adulter)
			return adulteryRes[Math.floor(Math.random() * 3)];
		else if(decieve)
			return deceptionRes[Math.floor(Math.random() * 3)];
		else
			return "1"
	}

	return "0"
};<commit_msg>additional moral principles<commit_after>module.exports = function(JSONObject) {
		// get our request information in string format
	var action = JSON.stringify(JSONObject.result.metadata.action)
	var spokenResult = JSON.stringify(JSONObject.result.metadata.speech)
	var parameters = JSON.stringify(JSONObject.result.metadata.parameters)
	var request = JSON.stringify(JSONObject.result.resolvedQuery)

		// initialize our universal principle responses
	var killingRes = ['Sorry, but I am programmed to know better. Killing is wrong in any context.', 'Dont you know that murder is abhorrent?', 'Sorry, murder is always immoral.']
	var harmingRes = ['The golden rule forbids it.', 'Did you not know that harming others is wrong?', 'Sorry, suffering is wrong, and if one can, they ought to prevent it.']
	var adulteryRes = ['Uh oh, adultery is not perimissible.', 'Sorry, lust and adultery are against my moral principles.', 'I am programmed to know better, as lustful behavior is wrong.']
	var deceptionRes = ['Deception is universally forbidden.', 'Nobody likes a liar.', 'Sorry, deception should never be allowed.']

		// initialize our word bank of forbidden universals
	var killing = ["kill", "murder", "assassinate", "destroy", "lynch", "slaughter", "slay", "annihilate"]
	var harming = ["shoot","butcher","massacre","rape","assualt","stab","punch","kick","brutalize", "injur", "assail", "jab","abuse","beat","harm","kidnap","torture"]
	var adultery = ["hooker", "prostitute", "rape", "brothel", "bimbo", "broad", "nympho", "tramp", "whore", "slut"]
	var deception = ["lie", "cheat", "decieve", "trick", "fake", "steal", "rob"]

		//initialize our word bank for types of action
	var badActs = ["smalltalk", "input.unknown", "messages.write", "shopping", "facebook","wisdom.person","wisdom.unknown"]
	var neutralActs = ["wisdom"]
	var goodActs = ["apps", "auth", "calculator", "device", "finance"]

		// initialize our flags
	var kill = false;
	var harm = false;
	var adulter = false;
	var decieve = false;
	var questionableAct = false;
	var goodAct = false;
	var neutralAct = false;

		// check our type of action for potential wrong doing
	if(action){
		badActs.forEach(function(word){
			if(action.indexOf(word) > -1)
				questionableAct = true
		})
		goodActs.forEach(function(word){
			if(action.indexOf(word) > -1)
				goodAct = true
		})
		neutralActs.forEach(function(word){
			if(action.indexOf(word) > -1)
				neutralAct = true
		})
	}
	else
		questionableAct = true

		// check the requested act for forbidden universals
	if(request){
		killing.forEach(function(word){
			if(request.indexOf(word) > -1)
				kill = true
		})
		harming.forEach(function(word){
			if(request.indexOf(word) > -1)
				harm = true
		})
		adultery.forEach(function(word){
			if(request.indexOf(word) > -1)
				adulter = true
		})
		deception.forEach(function(word){
			if(request.indexOf(word) > -1)
				decieve = true
		})
	}

		// if the user is looking to do something which is morally permissible, return 0
	if(goodAct)
		return "0"
	if(neutralAct)
		return "1"
		// if the user is engaging in something questionably permissible, check for forbidden universals
	if(questionableAct){
		if(kill)
			return killingRes[Math.floor(Math.random() * 3)];
		else if(harm)
			return harmingRes[Math.floor(Math.random() * 3)];
		else if(adulter)
			return adulteryRes[Math.floor(Math.random() * 3)];
		else if(decieve)
			return deceptionRes[Math.floor(Math.random() * 3)];
		else
			return "1"
	}

	return "0"
};<|endoftext|>


<commit_before>// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkID=397704
// To debug code on page load in Ripple or on Android devices/emulators: launch your app, set breakpoints,
// and then run "window.location.reload()" in the JavaScript Console.
(function () {
    "use strict";

    document.addEventListener('deviceready', onDeviceReady.bind(this), false);

    function onDeviceReady() {
        // Handle the Cordova pause and resume events
        document.addEventListener('pause', onPause.bind(this), false);
        document.addEventListener('resume', onResume.bind(this), false);

        // Initialize the Mobile Services client here.
        var client = new WindowsAzure.MobileServiceClient('https://todolist-cordova.azure-mobile.net/', 'ZPBJZlWeNQAMBTzibQiPHAHIVpeSut47'),
            todoItemTable = client.getTable('TodoItem');

        // Read current data and rebuild UI.
        // If you plan to generate complex UIs like this, consider using a JavaScript templating library.
        function refreshTodoItems() {
            $('#summary').html("Loading...");
            var query = todoItemTable;//.where({ complete: false });

            // Execute the query and then generate the array list.
            query.read().then(function (todoItems) {
                var listItems = $.map(todoItems, function (item) {
                    var listItem = $('
')
                        .attr('data-todoitem-id', item.id)
                        .append($('<button class="item-delete">Delete</button>'))
                        .append($('<input type="checkbox" class="item-complete">').prop('checked', item.complete))
                        .append($('
').append($('<input class="item-text">').val(item.text)));

                    // Only add the image if the URL exists.
                    if (item.imageUri)
                    {
                        listItem.append($('').attr('src', item.imageUri));
                    }
                    return listItem;
                });

                $('#todo-items').empty().append(listItems).toggle(listItems.length > 0);
                $('#summary').html('' + todoItems.length + ' item(s)');

                var width = $('#todo-items').width();

                $('#todo-items img').css({
                    'max-width': width, 'height': 'auto'
                });

            }, handleError);
        }

        function handleError(error) {
            alert("error: " + JSON.string(error));
            var text = error + (error.request ? ' - ' + error.request.status : '');
            $('#errorlog').append($('
').text(text));
        }

        function getTodoItemId(formElement) {
            return $(formElement).closest('li').attr('data-todoitem-id');
        }

        /******************************************************
        // Begin upload images to Azure Mobile Services additions.
        *********************************************************/
        // This is the new item being inserted.
        var insertedItem;

        // This function is called to get the newly captured image
        // file and read it into an array buffer.
        function readImage(capturedFile) {
            window.resolveLocalFileSystemURL(capturedFile.fullPath, function (fileEntry) {
                fileEntry.file(function (file) {
                    // We need a FileReader to read the captured file.
                    var reader = new FileReader();
                    reader.onloadend = readCompleted;
                    reader.onerror = fail;

                    // Read the captured file into a byte array.
                    // This function is not currently supported on Windows Phone.
                    reader.readAsArrayBuffer(file);
                }, fail);
            });
        }

        // This function gets called when the reader is done loading the image
        // and it is sent via an XMLHttpRequest to the Azure Storage Blob service.
        var readCompleted = function (evt) {
            if (evt.target.readyState == FileReader.DONE) {

                // The binary data is the result.
                var requestData = evt.target.result;

                // Build the request URI with the SAS, which gives us permissions to upload.
                var uriWithAccess = insertedItem.imageUri + "?" + insertedItem.sasQueryString;
                var xhr = new XMLHttpRequest();
                xhr.onerror = fail;
                xhr.onloadend = uploadCompleted;
                xhr.open("PUT", uriWithAccess);
                xhr.setRequestHeader('x-ms-blob-type', 'BlockBlob');
                xhr.setRequestHeader('x-ms-blob-content-type', 'image/jpeg');
                xhr.send(requestData);
            }
        }

        // This function is called when the XMLHttpRequest has a response.
        var uploadCompleted = function (r) {
            // Response code is 201 (Created) if success.
            if (r.currentTarget.status === 201) {

                console.debug("Upload complete.");

                // Refresh the UI with the latest image.
                refreshTodoItems();
            }
            else {
                alert("An error occurred during upload.");
            }
        }

        // Function that handles general errors.
        function fail(err) {
            alert("An error has occurred: " + JSON.stringify(err));
        }

        // Insert a new item, then also upload a captured image if we have one.
        var insertNewItemWithUpload = function (newItem, capturedFile) {
            // Do the insert so that we can get the SAS query string from Blob storage.
            todoItemTable.insert(newItem).then(function (item) {
                // If we have obtained an SAS, then upload the image to Blob storage.
                if (item.sasQueryString !== undefined) {

                    insertedItem = item;
                    readImage(capturedFile);
                }
            }, handleError).then(refreshTodoItems, handleError);
        }

        // Handle insert--this replaces the existing handler.
        $('#add-item').submit(function (evt) {
            var textbox = $('#new-item-text'),
                itemText = textbox.val();
            if (itemText !== '') {

                var newItem = { text: itemText, complete: false };
                // Do the capture before we do the insert. If user cancels, just continue.
                // Launch device camera application, allowing user to capture a single image.
                navigator.device.capture.captureImage(function (mediaFiles) {
                    if (mediaFiles) {
                        // Set a reference to the captured file.
                        var capturedFile = mediaFiles[0];
                        console.debug("capturedFile object: " + JSON.stringify(capturedFile));

                        // Set the properties we need on the inserted item.
                        newItem.containerName = "todoitemimages";
                        newItem.resourceName = capturedFile.name;

                        // Insert the item and upload the blob.
                        insertNewItemWithUpload(newItem, capturedFile);
                    }

                }, function () {
                    // Insert the item but not the blob.
                    insertNewItemWithUpload(newItem, null);
                }, { limit: 1 });
            }
            textbox.val('').focus();
            evt.preventDefault();
        });
        /******************************************************
        // End upload images to Azure Mobile Services additions.
        *********************************************************/

        $('#refresh').click(function (evt) {
            refreshTodoItems();
            evt.preventDefault();
        });

        // Handle update
        $(document.body).on('change', '.item-text', function () {
            var newText = $(this).val();
            todoItemTable.update({ id: getTodoItemId(this), text: newText }).then(null, handleError);
        });

        $(document.body).on('change', '.item-complete', function () {
            var isComplete = $(this).prop('checked');
            todoItemTable.update({ id: getTodoItemId(this), complete: isComplete }).then(refreshTodoItems, handleError);
        });

        // Handle delete
        $(document.body).on('click', '.item-delete', function () {
            todoItemTable.del({ id: getTodoItemId(this) }).then(refreshTodoItems, handleError);
        });

        // On initial load, start by fetching the current data
        refreshTodoItems();

    };

    function onPause() {
        // TODO: This application has been suspended. Save application state here.
    };

    function onResume() {
        // TODO: This application has been reactivated. Restore application state here.
    };
})();<commit_msg>Removed service-specific info (URL and app key)<commit_after>(function () {
    "use strict";

    document.addEventListener('deviceready', onDeviceReady.bind(this), false);

    function onDeviceReady() {
        // Handle the Cordova pause and resume events
        document.addEventListener('pause', onPause.bind(this), false);
        document.addEventListener('resume', onResume.bind(this), false);

        // Initialize the Mobile Services client here.
        var client = new WindowsAzure.MobileServiceClient('https://MY_MOBILE_SERVICE.azure-mobile.net/', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),
            todoItemTable = client.getTable('TodoItem');

        // Read current data and rebuild UI.
        // If you plan to generate complex UIs like this, consider using a JavaScript templating library.
        function refreshTodoItems() {
            $('#summary').html("Loading...");
            var query = todoItemTable;//.where({ complete: false });

            // Execute the query and then generate the array list.
            query.read().then(function (todoItems) {
                var listItems = $.map(todoItems, function (item) {
                    var listItem = $('
')
                        .attr('data-todoitem-id', item.id)
                        .append($('<button class="item-delete">Delete</button>'))
                        .append($('<input type="checkbox" class="item-complete">').prop('checked', item.complete))
                        .append($('
').append($('<input class="item-text">').val(item.text)));

                    // Only add the image if the URL exists.
                    if (item.imageUri)
                    {
                        listItem.append($('').attr('src', item.imageUri));
                    }
                    return listItem;
                });

                $('#todo-items').empty().append(listItems).toggle(listItems.length > 0);
                $('#summary').html('' + todoItems.length + ' item(s)');

                var width = $('#todo-items').width();

                $('#todo-items img').css({
                    'max-width': width, 'height': 'auto'
                });

            }, handleError);
        }

        function handleError(error) {
            alert("error: " + JSON.string(error));
            var text = error + (error.request ? ' - ' + error.request.status : '');
            $('#errorlog').append($('
').text(text));
        }

        function getTodoItemId(formElement) {
            return $(formElement).closest('li').attr('data-todoitem-id');
        }

        /******************************************************
        // Begin upload images to Azure Mobile Services additions.
        *********************************************************/
        // This is the new item being inserted.
        var insertedItem;

        // This function is called to get the newly captured image
        // file and read it into an array buffer.
        function readImage(capturedFile) {
            window.resolveLocalFileSystemURL(capturedFile.fullPath, function (fileEntry) {
                fileEntry.file(function (file) {
                    // We need a FileReader to read the captured file.
                    var reader = new FileReader();
                    reader.onloadend = readCompleted;
                    reader.onerror = fail;

                    // Read the captured file into a byte array.
                    // This function is not currently supported on Windows Phone.
                    reader.readAsArrayBuffer(file);
                }, fail);
            });
        }

        // This function gets called when the reader is done loading the image
        // and it is sent via an XMLHttpRequest to the Azure Storage Blob service.
        var readCompleted = function (evt) {
            if (evt.target.readyState == FileReader.DONE) {

                // The binary data is the result.
                var requestData = evt.target.result;

                // Build the request URI with the SAS, which gives us permissions to upload.
                var uriWithAccess = insertedItem.imageUri + "?" + insertedItem.sasQueryString;
                var xhr = new XMLHttpRequest();
                xhr.onerror = fail;
                xhr.onloadend = uploadCompleted;
                xhr.open("PUT", uriWithAccess);
                xhr.setRequestHeader('x-ms-blob-type', 'BlockBlob');
                xhr.setRequestHeader('x-ms-blob-content-type', 'image/jpeg');
                xhr.send(requestData);
            }
        }

        // This function is called when the XMLHttpRequest has a response.
        var uploadCompleted = function (r) {
            // Response code is 201 (Created) if success.
            if (r.currentTarget.status === 201) {

                console.debug("Upload complete.");

                // Refresh the UI with the latest image.
                refreshTodoItems();
            }
            else {
                alert("An error occurred during upload.");
            }
        }

        // Function that handles general errors.
        function fail(err) {
            alert("An error has occurred: " + JSON.stringify(err));
        }

        // Insert a new item, then also upload a captured image if we have one.
        var insertNewItemWithUpload = function (newItem, capturedFile) {
            // Do the insert so that we can get the SAS query string from Blob storage.
            todoItemTable.insert(newItem).then(function (item) {
                // If we have obtained an SAS, then upload the image to Blob storage.
                if (item.sasQueryString !== undefined) {

                    insertedItem = item;
                    readImage(capturedFile);
                }
            }, handleError).then(refreshTodoItems, handleError);
        }

        // Handle insert--this replaces the existing handler.
        $('#add-item').submit(function (evt) {
            var textbox = $('#new-item-text'),
                itemText = textbox.val();
            if (itemText !== '') {

                var newItem = { text: itemText, complete: false };
                // Do the capture before we do the insert. If user cancels, just continue.
                // Launch device camera application, allowing user to capture a single image.
                navigator.device.capture.captureImage(function (mediaFiles) {
                    if (mediaFiles) {
                        // Set a reference to the captured file.
                        var capturedFile = mediaFiles[0];
                        console.debug("capturedFile object: " + JSON.stringify(capturedFile));

                        // Set the properties we need on the inserted item.
                        newItem.containerName = "todoitemimages";
                        newItem.resourceName = capturedFile.name;

                        // Insert the item and upload the blob.
                        insertNewItemWithUpload(newItem, capturedFile);
                    }

                }, function () {
                    // Insert the item but not the blob.
                    insertNewItemWithUpload(newItem, null);
                }, { limit: 1 });
            }
            textbox.val('').focus();
            evt.preventDefault();
        });
        /******************************************************
        // End upload images to Azure Mobile Services additions.
        *********************************************************/

        $('#refresh').click(function (evt) {
            refreshTodoItems();
            evt.preventDefault();
        });

        // Handle update
        $(document.body).on('change', '.item-text', function () {
            var newText = $(this).val();
            todoItemTable.update({ id: getTodoItemId(this), text: newText }).then(null, handleError);
        });

        $(document.body).on('change', '.item-complete', function () {
            var isComplete = $(this).prop('checked');
            todoItemTable.update({ id: getTodoItemId(this), complete: isComplete }).then(refreshTodoItems, handleError);
        });

        // Handle delete
        $(document.body).on('click', '.item-delete', function () {
            todoItemTable.del({ id: getTodoItemId(this) }).then(refreshTodoItems, handleError);
        });

        // On initial load, start by fetching the current data
        refreshTodoItems();

    };

    function onPause() {
        // TODO: This application has been suspended. Save application state here.
    };

    function onResume() {
        // TODO: This application has been reactivated. Restore application state here.
    };
})();<|endoftext|>


<commit_before>/* eslint-disable */

const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')

const questions = [
  {
    type: 'input',
    name: 'appKey',
    message: 'Input the appKey for JPush'
  },
  {
    type: 'input',
    name: 'moduleName',
    message: 'Input the module name of android',
    default: 'app'
  }
]

inquirer.prompt(questions).then(answers => {
  const { appKey, moduleName } = answers

  //  深度遍历所有文件，
  getAllFiles('./ios', function(f, s) {
    var isAppdelegate = f.match(/AppDelegate\.m/)
    // 找到Appdelegate.m 文件 插入代码
    if (isAppdelegate != null) {
      console.log('the file is appdelegate:' + f)
      insertJPushCode(f, appKey)
    }
  })

  getAndroidManifest('./android/' + moduleName, function(f, s) {
    var isAndroidManifest = f.match(/AndroidManifest\.xml/)
    if (isAndroidManifest != null) {
      configureAndroidManifest(f)
    }
  })

  getConfigureFiles('./android', function(f, s) {
    // 找到settings.gradle
    var isSettingGradle = f.match(/settings\.gradle/)
    if (isSettingGradle != null) {
      console.log('find settings.gradle in android project ' + f)
      configureSetting(f)
    }

    // 找到project下的build.gradle
    var isProjectGradle = f.match(/.*\/build\.gradle/)
    if (isProjectGradle != null) {
      console.log('find build.gradle in android project ' + f)
      configureGradle(f)
      configureAppKey(f, appKey)
    }
  })
})

function insertJPushCode(file, appKey) {
  // 这个是插入代码的脚本 install
  if (!isFile(file)) {
    console.log('configuration JPush error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  // 删除所有的 JPush 相关代码  注册推送的没有删除，
  rf = rf.replace(/\n#import <RCTJPushModule.h>/, '')
  rf = rf.replace(/\n#ifdef NSFoundationVersionNumber_iOS_9_x_Max/, '')
  rf = rf.replace(/\n#import <UserNotifications\/UserNotifications\.h>/, '')
  rf = rf.replace(/\n#import <UserNotifications\/UserNotifications\.h>/, '')
  rf = rf.replace(/\n#endif/, '')
  rf = rf.replace(/\[JPUSHService registerDeviceToken:deviceToken\];\n/, '')

  // 插入 头文件
  rf = rf.replace(
    /#import "AppDelegate.h"/,
    '#import "AppDelegate.h"\n#import <RCTJPushModule.h>\n#ifdef NSFoundationVersionNumber_iOS_9_x_Max\n#import <UserNotifications/UserNotifications.h>\n#endif'
  )
  fs.writeFileSync(file, rf, 'utf-8')

  // 这个是删除代码的脚本 uninstall
  // var rf = fs.readFileSync(file,"utf-8");
  // rf = rf.replace(/#import "AppDelegate.h"[*\n]#import <RCTJPushModule.h>/,"\#import \"AppDelegate.h\"");
  // fs.writeFileSync(file, rf, "utf-8");

  // 插入 注册推送 和启动jpush sdk
  // - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  // {
  var rf = fs.readFileSync(file, 'utf-8')
  var searchDidLaunch = rf.match(/\n.*didFinishLaunchingWithOptions.*\n?\{/)
  if (searchDidLaunch == null) {
    console.log('没有匹配到 didFinishLaunchingWithOptions,将自动插入改方法')
    console.log(rf)
  } else {
    // console.log(searchDidlaunch[0]);
    var oldValue = rf.match(/\[JPUSHService registerForRemoteNotificationTypes/)
    if (oldValue == null) {
      rf = rf.replace(
        searchDidLaunch[0],
        searchDidLaunch[0] +
        '\nJPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];\n     entity.types = UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound;\n     [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];\n\
[JPUSHService setupWithOption:launchOptions appKey:@"' +
        appKey +
        '"\n\
                    channel:nil apsForProduction:true];'
      )
      fs.writeFileSync(file, rf, 'utf-8')
    }
  }

  //  这个插入代码 didRegisterForRemoteNotificationsWithDeviceToken
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*didRegisterForRemoteNotificationsWithDeviceToken:\(NSData \*\)deviceToken[ ]*\{/
  )

  if (search == null) {
    console.log(
      '没有匹配到 函数 didRegisterForRemoteNotificationsWithDeviceToken,将自动插入改方法'
    )
    rf = rf.replace(
      /@end/,
      '- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {\n[JPUSHService registerDeviceToken:deviceToken];\n}\n@end'
    )
    // console.log(rf);
    fs.writeFileSync(file, rf, 'utf-8')
  } else {
    console.log(search[0])
    var oldValue = rf.match(/\[JPUSHService registerDeviceToken/)
    if (oldValue == null) {
      rf = rf.replace(
        search[0],
        search[0] + '\n[JPUSHService registerDeviceToken:deviceToken];'
      )
      fs.writeFileSync(file, rf, 'utf-8')
    } else {
      console.log('registerDeviceToken存在，不在插入')
    }
  }

  // 这里插入 didReceiveRemoteNotification
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*didReceiveRemoteNotification:\(NSDictionary \*\)userInfo[ ]*\{/
  )
  if (search == null) {
    console.log('没有匹配到 函数 didReceiveRemoteNotification,将自动插入改方法')
    rf = rf.replace(
      /@end/,
      '- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {\n[[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:userInfo];\n}\n@end'
    )
    // console.log(rf);
    fs.writeFileSync(file, rf, 'utf-8')
  }

  // 这里插入 didReceiveRemoteNotification fetchCompletionHandler
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*didReceiveRemoteNotification:[ ]*\(NSDictionary \*\)[ ]*userInfo[ ]*fetchCompletionHandler:\(void[ ]*\(\^\)[ ]*\(UIBackgroundFetchResult\)\)completionHandler \{/
  )
  if (search == null) {
    console.log(
      '没有匹配到 函数 didReceiveRemoteNotification fetchCompletionHandler,将自动插入改方法'
    )
    rf = rf.replace(
      /\@end/,
      '- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)   (UIBackgroundFetchResult))completionHandler {\n[[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:userInfo];\n}\n@end'
    )
    // console.log(rf);
    fs.writeFileSync(file, rf, 'utf-8')
  }

  // 这里插入 willPresentNotification
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*willPresentNotification:\(UNNotification \*\)notification[ ]*withCompletionHandler:.*\{\n/
  )
  if (search == null) {
    console.log('没有匹配到 函数 willPresentNotification,将自动插入改方法')
    rf = rf.replace(
      /@end/,
      '- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler {\n NSDictionary * userInfo = notification.request.content.userInfo;\n  [JPUSHService handleRemoteNotification:userInfo];\n [[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:userInfo];\n    \n completionHandler(UNNotificationPresentationOptionAlert);\n}\n@end'
    )
    fs.writeFileSync(file, rf, 'utf-8')
  }

  // 这里插入 didReceiveNotificationResponse
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*jpushNotificationCenter:\(UNUserNotificationCenter \*\)center[ ]*didReceiveNotificationResponse:\(UNNotificationResponse \*\)response.*\{\n/
  )
  if (search == null) {
    console.log('没有匹配到 函数 didReceiveRemoteNotification,将自动插入改方法')
    rf = rf.replace(
      /@end/,
      '- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {\nNSDictionary * userInfo = response.notification.request.content.userInfo;\n[JPUSHService handleRemoteNotification:userInfo];\n[[NSNotificationCenter defaultCenter] postNotificationName:kJPFOpenNotification object:userInfo];\n\ncompletionHandler();\n}\n@end'
    )
    // console.log(rf);
    fs.writeFileSync(file, rf, 'utf-8')
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*didReceiveLocalNotification:\(UILocalNotification \*\)notification[ ]*\{/
  )
  if (search == null) {
    console.log('没有匹配到 函数 didReceiveLocalNotification,将自动插入改方法')
    rf = rf.replace(
      /@end/,
      '- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {\n[[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:notification.userInfo];\n}\n@end'
    )
    fs.writeFileSync(file, rf, 'utf-8')
  }
}

// 判断文件
function exists(file) {
  return fs.existsSync(file) || file.existsSync(file)
}

function isFile(file) {
  return exists(file) && fs.statSync(file).isFile()
}

// getAllfiles("./",function(f,s){
//   console.log(f);
//   var isAppdelegate = f.match(/AppDelegate\.m/);
//   var isiOSProjectPbxprojFile = f.match(/[.]*\.pbxproj/);
// });
// function to get all file
function getAllFiles(dir, findOne) {
  // if (arguments.length < 2) throw new TypeError('Bad arguments number');

  if (typeof findOne !== 'function') {
    throw new TypeError('The argument "findOne" must be a function')
  }

  eachFileSync(path.resolve(dir), findOne)
}

function eachFileSync(dir, findOne) {
  var stats = fs.statSync(dir)
  findOne(dir, stats)

  // 遍历子目录
  if (stats.isDirectory()) {
    var files = fullPath(dir, fs.readdirSync(dir))
    // console.log(dir);
    files.forEach(function(f) {
      eachFileSync(f, findOne)
    })
  }
}

function fullPath(dir, files) {
  return files.map(function(f) {
    return path.join(dir, f)
  })
}

function getAndroidManifest(dir, findOne) {
  if (typeof findOne !== 'function') {
    throw new TypeError('The argument "findOne" must be a function')
  }

  eachFileSync(path.resolve(dir), findOne)
}

function getConfigureFiles(dir, findOne) {
  if (typeof findOne !== 'function') {
    throw new TypeError('The argument "findOne" must be a function')
  }

  eachFileSync(path.resolve(dir), findOne)
}

function configureAndroidManifest(file) {
  if (!isFile(file)) {
    console.log('configuration JPush error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var isAlreadyWrite = rf.match(/.*android:value="\$\{JPUSH_APPKEY\}"/)
  if (isAlreadyWrite == null) {
    var searchKey = rf.match(/\n.*<\/activity>/)
    if (searchKey != null) {
      rf = rf.replace(
        searchKey[0],
        searchKey[0] +
        '\n\n<meta-data android:name="JPUSH_CHANNEL" android:value="${APP_CHANNEL}"/>\n<meta-data android:name="JPUSH_APPKEY" android:value="${JPUSH_APPKEY}"/>\n'
      )
      fs.writeFileSync(file, rf, 'utf-8')
    }
  }
}

function configureAppKey(file, appKey) {
  if (!isFile(file)) {
    console.log('configure AppKey error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var isAlreadyWrite = rf.match(/.*JPUSH_APPKEY.*/)
  if (isAlreadyWrite == null) {
    var insertKey = rf.match(/\n.*ndk \{\n.*\}\n/)
    if (insertKey != null) {
      rf = rf.replace(
        insertKey[0],
        insertKey[0] +
        'manifestPlaceholders = [\n            JPUSH_APPKEY: "' +
        appKey +
        '",\n            APP_CHANNEL: "developer-default"\n        ]\n'
      )
      fs.writeFileSync(file, rf, 'utf-8')
    } else {
      console.log('Configure appKey error, should configure manually.')
    }
  }
}

function configureSetting(file) {
  if (!isFile(file)) {
    console.log('configuration JPush error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var isAlreadyWrite = rf.match(/.*jpush-react-native.*/)
  if (isAlreadyWrite == null) {
    var re = new RegExp("\n.*include.*':" + moduleName + "'", 'gi')
    var searchKey = rf.match(re)
    if (searchKey != null) {
      rf = rf.replace(
        searchKey[0],
        searchKey[0] +
        ", ':jpush-react-native', ':jcore-react-native'\nproject(':jpush-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jpush-react-native/android')\nproject(':jcore-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jcore-react-native/android')\n"
      )
      fs.writeFileSync(file, rf, 'utf-8')
    } else {
      console.log('Did not find include in settings.gradle: ' + file)
    }
  }
}

function configureGradle(file) {
  if (!isFile(file)) {
    console.log('configuration JPush error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var isAlreadyWrite = rf.match(/.*jpush-react-native.*/)
  if (isAlreadyWrite == null) {
    var searchKey = rf.match(/\n.*compile fileTree.*\n/)
    if (searchKey != null) {
      rf = rf.replace(
        searchKey[0],
        searchKey[0] +
        "    compile project(':jpush-react-native')\n    compile project(':jcore-react-native')\n"
      )
      fs.writeFileSync(file, rf, 'utf-8')
    } else {
      console.log('Did not find "compile" in ' + file)
    }
  }
}
<commit_msg>fix JPushConfiguration bug<commit_after>/* eslint-disable */

const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')

const questions = [
  {
    type: 'input',
    name: 'appKey',
    message: 'Input the appKey for JPush'
  },
  {
    type: 'input',
    name: 'moduleName',
    message: 'Input the module name of android',
    default: 'app'
  }
]

inquirer.prompt(questions).then(answers => {
  const { appKey, moduleName } = answers
  if (moduleName == undefined) {
    moduleName = "app"
  }

  //  深度遍历所有文件，
  getAllFiles('./ios', function(f, s) {
    var isAppdelegate = f.match(/AppDelegate\.m/)
    // 找到Appdelegate.m 文件 插入代码
    if (isAppdelegate != null) {
      console.log('the file is appdelegate:' + f)
      insertJPushCode(f, appKey)
    }
  })

  getAndroidManifest('./android/' + moduleName, function(f, s) {
    var isAndroidManifest = f.match(/AndroidManifest\.xml/)
    if (isAndroidManifest != null) {
      configureAndroidManifest(f)
    }
  })

  getConfigureFiles('./android', function(f, s) {
    // 找到settings.gradle
    var isSettingGradle = f.match(/settings\.gradle/)
    if (isSettingGradle != null) {
      console.log('find settings.gradle in android project ' + f)
      configureSetting(f, moduleName)
    }

    // 找到project下的build.gradle
    var isProjectGradle = f.match(/.*\/build\.gradle/)
    if (isProjectGradle != null) {
      console.log('find build.gradle in android project ' + f)
      configureGradle(f)
      configureAppKey(f, appKey)
    }
  })
})

function insertJPushCode(file, appKey) {
  // 这个是插入代码的脚本 install
  if (!isFile(file)) {
    console.log('configuration JPush error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  // 删除所有的 JPush 相关代码  注册推送的没有删除，
  rf = rf.replace(/\n#import <RCTJPushModule.h>/, '')
  rf = rf.replace(/\n#ifdef NSFoundationVersionNumber_iOS_9_x_Max/, '')
  rf = rf.replace(/\n#import <UserNotifications\/UserNotifications\.h>/, '')
  rf = rf.replace(/\n#import <UserNotifications\/UserNotifications\.h>/, '')
  rf = rf.replace(/\n#endif/, '')
  rf = rf.replace(/\[JPUSHService registerDeviceToken:deviceToken\];\n/, '')

  // 插入 头文件
  rf = rf.replace(
    /#import "AppDelegate.h"/,
    '#import "AppDelegate.h"\n#import <RCTJPushModule.h>\n#ifdef NSFoundationVersionNumber_iOS_9_x_Max\n#import <UserNotifications/UserNotifications.h>\n#endif'
  )
  fs.writeFileSync(file, rf, 'utf-8')

  // 这个是删除代码的脚本 uninstall
  // var rf = fs.readFileSync(file,"utf-8");
  // rf = rf.replace(/#import "AppDelegate.h"[*\n]#import <RCTJPushModule.h>/,"\#import \"AppDelegate.h\"");
  // fs.writeFileSync(file, rf, "utf-8");

  // 插入 注册推送 和启动jpush sdk
  // - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  // {
  var rf = fs.readFileSync(file, 'utf-8')
  var searchDidLaunch = rf.match(/\n.*didFinishLaunchingWithOptions.*\n?\{/)
  if (searchDidLaunch == null) {
    console.log('没有匹配到 didFinishLaunchingWithOptions,将自动插入改方法')
    console.log(rf)
  } else {
    // console.log(searchDidlaunch[0]);
    var oldValue = rf.match(/\[JPUSHService registerForRemoteNotificationTypes/)
    if (oldValue == null) {
      rf = rf.replace(
        searchDidLaunch[0],
        searchDidLaunch[0] +
        '\nJPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];\n     entity.types = UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound;\n     [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];\n\
[JPUSHService setupWithOption:launchOptions appKey:@"' +
        appKey +
        '"\n\
                    channel:nil apsForProduction:true];'
      )
      fs.writeFileSync(file, rf, 'utf-8')
    }
  }

  //  这个插入代码 didRegisterForRemoteNotificationsWithDeviceToken
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*didRegisterForRemoteNotificationsWithDeviceToken:\(NSData \*\)deviceToken[ ]*\{/
  )

  if (search == null) {
    console.log(
      '没有匹配到 函数 didRegisterForRemoteNotificationsWithDeviceToken,将自动插入改方法'
    )
    rf = rf.replace(
      /@end/,
      '- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {\n[JPUSHService registerDeviceToken:deviceToken];\n}\n@end'
    )
    // console.log(rf);
    fs.writeFileSync(file, rf, 'utf-8')
  } else {
    console.log(search[0])
    var oldValue = rf.match(/\[JPUSHService registerDeviceToken/)
    if (oldValue == null) {
      rf = rf.replace(
        search[0],
        search[0] + '\n[JPUSHService registerDeviceToken:deviceToken];'
      )
      fs.writeFileSync(file, rf, 'utf-8')
    } else {
      console.log('registerDeviceToken存在，不在插入')
    }
  }

  // 这里插入 didReceiveRemoteNotification
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*didReceiveRemoteNotification:\(NSDictionary \*\)userInfo[ ]*\{/
  )
  if (search == null) {
    console.log('没有匹配到 函数 didReceiveRemoteNotification,将自动插入改方法')
    rf = rf.replace(
      /@end/,
      '- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {\n[[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:userInfo];\n}\n@end'
    )
    // console.log(rf);
    fs.writeFileSync(file, rf, 'utf-8')
  }

  // 这里插入 didReceiveRemoteNotification fetchCompletionHandler
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*didReceiveRemoteNotification:[ ]*\(NSDictionary \*\)[ ]*userInfo[ ]*fetchCompletionHandler:\(void[ ]*\(\^\)[ ]*\(UIBackgroundFetchResult\)\)completionHandler \{/
  )
  if (search == null) {
    console.log(
      '没有匹配到 函数 didReceiveRemoteNotification fetchCompletionHandler,将自动插入改方法'
    )
    rf = rf.replace(
      /\@end/,
      '- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)   (UIBackgroundFetchResult))completionHandler {\n[[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:userInfo];\n}\n@end'
    )
    // console.log(rf);
    fs.writeFileSync(file, rf, 'utf-8')
  }

  // 这里插入 willPresentNotification
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*willPresentNotification:\(UNNotification \*\)notification[ ]*withCompletionHandler:.*\{\n/
  )
  if (search == null) {
    console.log('没有匹配到 函数 willPresentNotification,将自动插入改方法')
    rf = rf.replace(
      /@end/,
      '- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler {\n NSDictionary * userInfo = notification.request.content.userInfo;\n  [JPUSHService handleRemoteNotification:userInfo];\n [[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:userInfo];\n    \n completionHandler(UNNotificationPresentationOptionAlert);\n}\n@end'
    )
    fs.writeFileSync(file, rf, 'utf-8')
  }

  // 这里插入 didReceiveNotificationResponse
  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*jpushNotificationCenter:\(UNUserNotificationCenter \*\)center[ ]*didReceiveNotificationResponse:\(UNNotificationResponse \*\)response.*\{\n/
  )
  if (search == null) {
    console.log('没有匹配到 函数 didReceiveRemoteNotification,将自动插入改方法')
    rf = rf.replace(
      /@end/,
      '- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {\nNSDictionary * userInfo = response.notification.request.content.userInfo;\n[JPUSHService handleRemoteNotification:userInfo];\n[[NSNotificationCenter defaultCenter] postNotificationName:kJPFOpenNotification object:userInfo];\n\ncompletionHandler();\n}\n@end'
    )
    // console.log(rf);
    fs.writeFileSync(file, rf, 'utf-8')
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var search = rf.match(
    /\n.*didReceiveLocalNotification:\(UILocalNotification \*\)notification[ ]*\{/
  )
  if (search == null) {
    console.log('没有匹配到 函数 didReceiveLocalNotification,将自动插入改方法')
    rf = rf.replace(
      /@end/,
      '- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {\n[[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:notification.userInfo];\n}\n@end'
    )
    fs.writeFileSync(file, rf, 'utf-8')
  }
}

// 判断文件
function exists(file) {
  return fs.existsSync(file) || file.existsSync(file)
}

function isFile(file) {
  return exists(file) && fs.statSync(file).isFile()
}

// getAllfiles("./",function(f,s){
//   console.log(f);
//   var isAppdelegate = f.match(/AppDelegate\.m/);
//   var isiOSProjectPbxprojFile = f.match(/[.]*\.pbxproj/);
// });
// function to get all file
function getAllFiles(dir, findOne) {
  // if (arguments.length < 2) throw new TypeError('Bad arguments number');

  if (typeof findOne !== 'function') {
    throw new TypeError('The argument "findOne" must be a function')
  }

  eachFileSync(path.resolve(dir), findOne)
}

function eachFileSync(dir, findOne) {
  var stats = fs.statSync(dir)
  findOne(dir, stats)

  // 遍历子目录
  if (stats.isDirectory()) {
    var files = fullPath(dir, fs.readdirSync(dir))
    // console.log(dir);
    files.forEach(function(f) {
      eachFileSync(f, findOne)
    })
  }
}

function fullPath(dir, files) {
  return files.map(function(f) {
    return path.join(dir, f)
  })
}

function getAndroidManifest(dir, findOne) {
  if (typeof findOne !== 'function') {
    throw new TypeError('The argument "findOne" must be a function')
  }

  eachFileSync(path.resolve(dir), findOne)
}

function getConfigureFiles(dir, findOne) {
  if (typeof findOne !== 'function') {
    throw new TypeError('The argument "findOne" must be a function')
  }

  eachFileSync(path.resolve(dir), findOne)
}

function configureAndroidManifest(file) {
  if (!isFile(file)) {
    console.log('configuration JPush error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var isAlreadyWrite = rf.match(/.*android:value="\$\{JPUSH_APPKEY\}"/)
  if (isAlreadyWrite == null) {
    var searchKey = rf.match(/\n.*<\/activity>/)
    if (searchKey != null) {
      rf = rf.replace(
        searchKey[0],
        searchKey[0] +
        '\n\n<meta-data android:name="JPUSH_CHANNEL" android:value="${APP_CHANNEL}"/>\n<meta-data android:name="JPUSH_APPKEY" android:value="${JPUSH_APPKEY}"/>\n'
      )
      fs.writeFileSync(file, rf, 'utf-8')
    }
  }
}

function configureAppKey(file, appKey) {
  if (!isFile(file)) {
    console.log('configure AppKey error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var isAlreadyWrite = rf.match(/.*JPUSH_APPKEY.*/)
  if (isAlreadyWrite == null) {
  var insertKey = rf.match(/.*versionName.*\n/)
    if (insertKey != null) {
      rf = rf.replace(
        insertKey[0],
        insertKey[0] +
        '        manifestPlaceholders = [\n            JPUSH_APPKEY: "' +
        appKey +
        '",\n            APP_CHANNEL: "developer-default"\n        ]\n'
      )
      fs.writeFileSync(file, rf, 'utf-8')
    } else {
      console.log('Configure appKey error, should configure manually.')
    }
  }
}

function configureSetting(file, moduleName) {
  if (!isFile(file)) {
    console.log('configuration JPush error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var isAlreadyWrite = rf.match(/.*jpush-react-native.*/)
  if (isAlreadyWrite == null) {
    var re = new RegExp("\n.*include.*':" + moduleName + "'", 'gi')
    var searchKey = rf.match(re)
    if (searchKey != null) {
      rf = rf.replace("\n", "\ninclude ':jpush-react-native' \nproject(':jpush-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jpush-react-native/android')\ninclude ':jcore-react-native' \nproject(':jcore-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jcore-react-native/android')\n")
      fs.writeFileSync(file, rf, 'utf-8')
    } else {
      console.log('Did not find include in settings.gradle: ' + file)
    }
  }
}

function configureGradle(file) {
  if (!isFile(file)) {
    console.log('configuration JPush error!!')
    return
  }

  var rf = fs.readFileSync(file, 'utf-8')
  var isAlreadyWrite = rf.match(/.*jpush-react-native.*/)
  if (isAlreadyWrite == null) {
    var searchKey = rf.match(/\n.*compile fileTree.*\n/)
    if (searchKey != null) {
      rf = rf.replace(
        searchKey[0],
        searchKey[0] +
        "    compile project(':jpush-react-native')\n    compile project(':jcore-react-native')\n"
      )
      fs.writeFileSync(file, rf, 'utf-8')
    } else {
      console.log('Did not find "compile" in ' + file)
    }
  }
}
<|endoftext|>


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

(function() {

  var uiEnabled = true;

  DrupalCon.ui.createPresentersWindow = function(tabGroup) {
    var PresentersWindow = Titanium.UI.createWindow({
      id: 'presentersWindow',
      title: 'Speakers',
      backgroundColor: '#FFF',
      barColor: '#414444',
      tabGroup: tabGroup
    });

    var nameList = getNameList();

    // I want our list of names to have the usernames mixed in, and they usually
    // start with lowercase, so we need to create a custom sortorder that ignores case.
    var sortedNames = nameList.sort(function(a, b) {
      a = a.toLowerCase();
      b = b.toLowerCase();
      if (a > b) { return 1; }
      if (a < b) { return -1; }
      return 0;
    });

    // Now we can do something, like, oh I don't know, build the table :)
    var headerLetter = '';
    var index = [];
    var index2 = [];
    var presenterRow = [];
    var data = [];

    for (var i in sortedNames) {
      var user = sortedNames[i].split(':');
      var uid = parseInt(user[1]) + 0;
      var fullName = user[0] + '';

      var shortName = user[2] + '';
      var name = shortName;
      if (fullName.charAt(fullName.length-2) == ',') {
        fullName = fullName.slice(0, fullName.length - 2);
      }
      else {
        name = fullName;
      }

      presenterRow = Ti.UI.createTableViewRow({
        hasChild: true,
        selectedColor: '#fff',
        backgroundColor: '#fff',
        backgroundSelectedColor: '#0779BE',
        color: '#000',
        name: name,
        uid: uid,
        height: 40,
        layout: 'auto'
      });

      if (fullName == shortName) {
        fullName = '';
      }
      else {
        fullName = cleanSpecialChars(fullName);
        var firstLastName = fullName.split(', ');
        fullName = firstLastName[1] + ' ' + firstLastName[0];
        shortName = "(" + shortName + ")";
        var lastName = firstLastName[0];
        var firstName = firstLastName[1];
      }

      // Android can't handle some of this label manipulation
      if (isAndroid()) {
        presenterRow.add(Ti.UI.createLabel({
        text: fullName, // + '  ' + shortName,
        fontFamily:'sans-serif',
        left: (fullName != '') ? 9 : 0,
        height: 40,
        color: '#000'
        }));

      }
      else {
        // iPhone - make it fancy
        var firstLeft = 0;
        var lastLeft = 0;

        if (fullName != '') {
          var firstNameLabel = Ti.UI.createLabel({
            text: firstName,
            font:'Helvetica',
            left: 10,
            height: 40,
            color: '#000'
          });
          presenterRow.add(firstNameLabel);
          firstLeft = firstNameLabel.toImage().width+10;

          var lastNameLabel = Ti.UI.createLabel({
            text: lastName,
            font:'Helvetica-Bold',
            left: firstLeft + 5,
            height: 40,
            color: '#000'
          });
          presenterRow.add(lastNameLabel);
          lastLeft = firstLeft + lastNameLabel.toImage().width;
        }

        // if (shortName != '') {
          // var shortNameLabel = Ti.UI.createLabel({
            // text: shortName,
            // font:'Helvetica',
            // left: lastLeft+10,
            // height: 40,
            // color: '#666'
          // });
          // presenterRow.add(shortNameLabel);
        // }

      }

      // If there is a new last name first letter, insert a header in the table.
      // We also push a new index so we can create a right side index for iphone.
      if (headerLetter == '' || name.charAt(0).toUpperCase() != headerLetter) {
        headerLetter = name.charAt(0).toUpperCase();
        presenterRow.header = headerLetter;
        index.push({title:headerLetter,index:i});
        index2.push({title:headerLetter+headerLetter,index:i});
      }

      data.push(presenterRow);
    }

    // Create the table view
    var tableview = Titanium.UI.createTableView({
      backgroundColor: '#fff',
      data: data
    });
    tableview.index = index;

    PresentersWindow.addEventListener('focus', function() {
      uiEnabled = true;
    });

    // create table view event listener
    tableview.addEventListener('click', function(e) {
      if (uiEnabled) {
        uiEnabled = false;
        if (e.index == 0) {
          tableview.index = index2;
        }
        // event data
        var index = e.index;
        var currentTab = (Ti.Platform.name == 'android') ? Titanium.UI.currentTab : PresentersWindow.tabGroup.activeTab;
        currentTab.open(DrupalCon.ui.createPresenterDetailWindow({
          title: e.rowData.name,
          uid: e.rowData.uid,
          name: e.rowData.name,
          tabGroup: currentTab
        }), {animated:true});
      }
    });

    // add table view to the window
    PresentersWindow.add(tableview);

    return PresentersWindow;
  };

  function getNameList() {
    var conn = Drupal.db.getConnection('main');
    var rows = conn.query("SELECT uid, name, full_name FROM user");

    // As far as I can tell, objects aren't allowed to be sorted, so even though
    // I can write a sort on say a.lastName - it won't stay sorted (yes I tried)
    // so I have to build an array, sort it, then decompile it to use it.
    // Have I mentioned lately that javascript is not my favorite language right now?
    var nameList = [];
    if (rows) {
      while (rows.isValidRow()) {
        var uid = rows.fieldByName('uid');
        //dpm(rows.fieldByName('full_name'));
        var full = rows.fieldByName('full_name');
        if (full) {
          var names = rows.fieldByName('full_name').split(' ');
          var lastName = names[names.length -1];
          var firstName = full.substr(0,full.length - (lastName.length + 1));
          nameList.push(lastName + ', ' + firstName + ':' + rows.fieldByName('uid') + ':' + rows.fieldByName('name'));
        }
        else {
          nameList.push(rows.fieldByName('name') + ':' + rows.fieldByName('uid') + ':' + rows.fieldByName('name'));
        }
        rows.next();
      }
      rows.close();
    }

    return nameList;
  }

})();
<commit_msg>stopped header shortcut letters from doubling up when first entry is clicked<commit_after>/**
 * This file is part of DrupalCon Mobile.
 *
 * DrupalCon Mobile is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DrupalCon Mobile is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with DrupalCon Mobile.  If not, see <http://www.gnu.org/licenses/>.
 */

(function() {

  var uiEnabled = true;

  DrupalCon.ui.createPresentersWindow = function(tabGroup) {
    var PresentersWindow = Titanium.UI.createWindow({
      id: 'presentersWindow',
      title: 'Speakers',
      backgroundColor: '#FFF',
      barColor: '#414444',
      tabGroup: tabGroup
    });

    var nameList = getNameList();

    // I want our list of names to have the usernames mixed in, and they usually
    // start with lowercase, so we need to create a custom sortorder that ignores case.
    var sortedNames = nameList.sort(function(a, b) {
      a = a.toLowerCase();
      b = b.toLowerCase();
      if (a > b) { return 1; }
      if (a < b) { return -1; }
      return 0;
    });

    // Now we can do something, like, oh I don't know, build the table :)
    var headerLetter = '';
    var index = [];
    var index2 = [];
    var presenterRow = [];
    var data = [];

    for (var i in sortedNames) {
      var user = sortedNames[i].split(':');
      var uid = parseInt(user[1]) + 0;
      var fullName = user[0] + '';

      var shortName = user[2] + '';
      var name = shortName;
      if (fullName.charAt(fullName.length-2) == ',') {
        fullName = fullName.slice(0, fullName.length - 2);
      }
      else {
        name = fullName;
      }

      presenterRow = Ti.UI.createTableViewRow({
        hasChild: true,
        selectedColor: '#fff',
        backgroundColor: '#fff',
        backgroundSelectedColor: '#0779BE',
        color: '#000',
        name: name,
        uid: uid,
        height: 40,
        layout: 'auto'
      });

      if (fullName == shortName) {
        fullName = '';
      }
      else {
        fullName = cleanSpecialChars(fullName);
        var firstLastName = fullName.split(', ');
        fullName = firstLastName[1] + ' ' + firstLastName[0];
        shortName = "(" + shortName + ")";
        var lastName = firstLastName[0];
        var firstName = firstLastName[1];
      }

      // Android can't handle some of this label manipulation
      if (isAndroid()) {
        presenterRow.add(Ti.UI.createLabel({
        text: fullName, // + '  ' + shortName,
        fontFamily:'sans-serif',
        left: (fullName != '') ? 9 : 0,
        height: 40,
        color: '#000'
        }));

      }
      else {
        // iPhone - make it fancy
        var firstLeft = 0;
        var lastLeft = 0;

        if (fullName != '') {
          var firstNameLabel = Ti.UI.createLabel({
            text: firstName,
            font:'Helvetica',
            left: 10,
            height: 40,
            color: '#000'
          });
          presenterRow.add(firstNameLabel);
          firstLeft = firstNameLabel.toImage().width+10;

          var lastNameLabel = Ti.UI.createLabel({
            text: lastName,
            font:'Helvetica-Bold',
            left: firstLeft + 5,
            height: 40,
            color: '#000'
          });
          presenterRow.add(lastNameLabel);
          lastLeft = firstLeft + lastNameLabel.toImage().width;
        }

        // if (shortName != '') {
          // var shortNameLabel = Ti.UI.createLabel({
            // text: shortName,
            // font:'Helvetica',
            // left: lastLeft+10,
            // height: 40,
            // color: '#666'
          // });
          // presenterRow.add(shortNameLabel);
        // }

      }

      // If there is a new last name first letter, insert a header in the table.
      // We also push a new index so we can create a right side index for iphone.
      if (headerLetter == '' || name.charAt(0).toUpperCase() != headerLetter) {
        headerLetter = name.charAt(0).toUpperCase();
    	presenterRow.header = headerLetter;
    	index.push({title:headerLetter,index:i});
    	index2.push({title:headerLetter+headerLetter,index:i});
      }

      data.push(presenterRow);
    }

    // Create the table view
    var tableview = Titanium.UI.createTableView({
      backgroundColor: '#fff',
      data: data
    });
    tableview.index = index;

    PresentersWindow.addEventListener('focus', function() {
      uiEnabled = true;
    });

    // create table view event listener
    tableview.addEventListener('click', function(e) {
      if (uiEnabled) {
        uiEnabled = false;
        // if (e.index == 0) {
          // tableview.index = index2;
        // }

        // event data
        var index = e.index;
        var currentTab = (Ti.Platform.name == 'android') ? Titanium.UI.currentTab : PresentersWindow.tabGroup.activeTab;
        currentTab.open(DrupalCon.ui.createPresenterDetailWindow({
          title: e.rowData.name,
          uid: e.rowData.uid,
          name: e.rowData.name,
          tabGroup: currentTab
        }), {animated:true});
      }
    });

    // add table view to the window
    PresentersWindow.add(tableview);

    return PresentersWindow;
  };

  function getNameList() {
    var conn = Drupal.db.getConnection('main');
    var rows = conn.query("SELECT uid, name, full_name FROM user");

    // As far as I can tell, objects aren't allowed to be sorted, so even though
    // I can write a sort on say a.lastName - it won't stay sorted (yes I tried)
    // so I have to build an array, sort it, then decompile it to use it.
    // Have I mentioned lately that javascript is not my favorite language right now?
    var nameList = [];
    if (rows) {
      while (rows.isValidRow()) {
        var uid = rows.fieldByName('uid');
        //dpm(rows.fieldByName('full_name'));
        var full = rows.fieldByName('full_name');
        if (full) {
          var names = rows.fieldByName('full_name').split(' ');
          var lastName = names[names.length -1];
          var firstName = full.substr(0,full.length - (lastName.length + 1));
          nameList.push(lastName + ', ' + firstName + ':' + rows.fieldByName('uid') + ':' + rows.fieldByName('name'));
        }
        else {
          nameList.push(rows.fieldByName('name') + ':' + rows.fieldByName('uid') + ':' + rows.fieldByName('name'));
        }
        rows.next();
      }
      rows.close();
    }

    return nameList;
  }

})();
<|endoftext|>


<commit_before>$(function() {

    // there are two levels of vocabulary auto complete.
    // currently we have this externally hosted vocabulary
    // for geonames.  I'm not going to make these any easier
    // to implement for an external url (it's all hard coded)
    // because I'm guessing we'll get away from the hard coding
  var cities_autocomplete_opts = {
    source: function( request, response ) {
      $.ajax( {
        url: "http://ws.geonames.org/searchJSON",
        dataType: "jsonp",
        data: {
          featureClass: "P",
          style: "full",
          maxRows: 12,
          name_startsWith: request.term
        },
        success: function( data ) {
          response( $.map( data.geonames, function( item ) {
            return {
              label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
              value: item.name
            }
          }));
        }
      });
    },
    minLength: 2,
  }
  $("#generic_file_based_near").autocomplete(cities_autocomplete_opts);

  function get_autocomplete_opts(field)
  {
    var autocomplete_opts = {
      minLength: 2,
      source: function( request, response ) {
        $.getJSON( "/authorities/generic_files/" + field, {
          q: request.term
        }, response );
      },
      focus: function() {
        // prevent value inserted on focus
        return false;
      }/*
      select: function( event, ui ) {
        $("#selectedSubjects").append("
" + ui.item.label+"
"+ui.item.value+"

");
        $(this).val("");
        return false;
      }*/
    }
    return autocomplete_opts;
  }

  var autocomplete_vocab = new Object();
  // the url variable to pass to determine the vocab to attach to
  autocomplete_vocab.url_var =      ['subject', 'language', 'tag'];
  // the form name to attach the event for autocomplete
  autocomplete_vocab.field_name =   ['generic_file_subject', 'generic_file_language', 'generic_file_tag'];
  // the id of the button pressed when adding an additional form element that has an autocomplete vocab
  autocomplete_vocab.add_btn_id =   ['additional_subject_submit', 'additional_language_submit', 'additional_tag_submit'];

  // loop over the autocomplete fields and attach the
  // events for autocomplete
  for (var i=0; i < autocomplete_vocab.url_var.length; i++)
  {
    // autocompletes
    $("#" + autocomplete_vocab.field_name[i])
        // don't navigate away from the field on tab when selecting an item
        .bind( "keydown", function( event ) {
            if ( event.keyCode === $.ui.keyCode.TAB &&
                    $( this ).data( "autocomplete" ).menu.active ) {
                event.preventDefault();
            }
        })
        .autocomplete( get_autocomplete_opts(autocomplete_vocab.url_var[i]) );

  }

  /*
   * adds a new file input for ingest
   */
  $('#additional_files_submit').click(function() {
    var a = $('#upload_field_1').clone();
    var currentIdNum = $('#file_count').attr("value");
    var nextIdNum = parseInt(currentIdNum)+1;
    a.attr("id", "upload_field_" + nextIdNum);
    var newFileInput = a.find('#Filedata_');
    newFileInput.attr("id", "Filedata_"+nextIdNum);
    $('#additional_files').append(a);
    $('#file_count').attr("value", nextIdNum);
    return false;
  })

  $('.adder').click(function() {
    //this.id = additional_N_submit
    //id for element to clone = additional_N_clone
    //id for element to append to = additional_N_elements
    var cloneId = this.id.replace("submit", "clone");
    var newId = this.id.replace("submit", "elements");
    var cloneElem = $('#'+cloneId).clone();

    //remove the button before adding the input
    //we don't want a button on each element being appended
    cloneElem.find('#'+this.id).remove();

    //clear out the value for the element being appended
    //so the new element has a blank value
    cloneElem.find('input[type=text]').attr("value", "");

    // should we attach an auto complete based on the input
    if (this.id == 'additional_based_near_submit') {
      cloneElem.find('input[type=text]').autocomplete(cities_autocomplete_opts);
    }
    else if ( (index = $.inArray(this.id, autocomplete_vocab.add_btn_id)) != -1 ) {
      cloneElem.find('input[type=text]').autocomplete(get_autocomplete_opts(autocomplete_vocab.url_var[index]));
    }

    $('#'+newId).append(cloneElem);
    cloneElem.find('input[type=text]').focus();
    return false;
  });

  $('.icon-plus').click(function() {
    //this.id format: "expand_id:NNNNNNNNNN"
    var a = this.id.split("expand_id_");
    if (a.length > 1)
    {
      docId = a[1]
      $("#detail_id_"+docId).toggle();
      if( $("#detail_id_"+docId).is(":hidden") ) {
        $("#expand_id_"+docId).attr("class", "icon-plus");
      }
      else {
        $("#expand_id_"+docId).attr("class", "icon-minus");
      }
    }
  })

});
<commit_msg>made adding a local vocab a bit easier in gamma.js.  also changed the autocomplete for geonames to add full city. fixes #686<commit_after>$(function() {

    // there are two levels of vocabulary auto complete.
    // currently we have this externally hosted vocabulary
    // for geonames.  I'm not going to make these any easier
    // to implement for an external url (it's all hard coded)
    // because I'm guessing we'll get away from the hard coding
  var cities_autocomplete_opts = {
    source: function( request, response ) {
      $.ajax( {
        url: "http://ws.geonames.org/searchJSON",
        dataType: "jsonp",
        data: {
          featureClass: "P",
          style: "full",
          maxRows: 12,
          name_startsWith: request.term
        },
        success: function( data ) {
          response( $.map( data.geonames, function( item ) {
            return {
              label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
              value: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
            }
          }));
        }
      });
    },
    minLength: 2,
  }
  $("#generic_file_based_near").autocomplete(cities_autocomplete_opts);

  function get_autocomplete_opts(field)
  {
    var autocomplete_opts = {
      minLength: 2,
      source: function( request, response ) {
        $.getJSON( "/authorities/generic_files/" + field, {
          q: request.term
        }, response );
      },
      focus: function() {
        // prevent value inserted on focus
        return false;
      }/*
      select: function( event, ui ) {
        $("#selectedSubjects").append("
" + ui.item.label+"
"+ui.item.value+"

");
        $(this).val("");
        return false;
      }*/
    }
    return autocomplete_opts;
  }

  var autocomplete_vocab = new Object();

  autocomplete_vocab.url_var =      ['subject', 'language', 'tag'];   // the url variable to pass to determine the vocab to attach to
  autocomplete_vocab.field_name = new Array(); // the form name to attach the event for autocomplete
  autocomplete_vocab.add_btn_id = new Array(); // the id of the button pressed when adding an additional form element

  // loop over the autocomplete fields and attach the
  // events for autocomplete and create other array values for autocomplete
  for (var i=0; i < autocomplete_vocab.url_var.length; i++)
  {
    autocomplete_vocab.field_name.push('generic_file_' + autocomplete_vocab.url_var[i]);
    autocomplete_vocab.add_btn_id.push('additional_' + autocomplete_vocab.url_var[i] + '_submit');
    // autocompletes
    $("#" + autocomplete_vocab.field_name[i])
        // don't navigate away from the field on tab when selecting an item
        .bind( "keydown", function( event ) {
            if ( event.keyCode === $.ui.keyCode.TAB &&
                    $( this ).data( "autocomplete" ).menu.active ) {
                event.preventDefault();
            }
        })
        .autocomplete( get_autocomplete_opts(autocomplete_vocab.url_var[i]) );

  }

  /*
   * adds a new file input for ingest
   */
  $('#additional_files_submit').click(function() {
    var a = $('#upload_field_1').clone();
    var currentIdNum = $('#file_count').attr("value");
    var nextIdNum = parseInt(currentIdNum)+1;
    a.attr("id", "upload_field_" + nextIdNum);
    var newFileInput = a.find('#Filedata_');
    newFileInput.attr("id", "Filedata_"+nextIdNum);
    $('#additional_files').append(a);
    $('#file_count').attr("value", nextIdNum);
    return false;
  })

  $('.adder').click(function() {
    //this.id = additional_N_submit
    //id for element to clone = additional_N_clone
    //id for element to append to = additional_N_elements
    var cloneId = this.id.replace("submit", "clone");
    var newId = this.id.replace("submit", "elements");
    var cloneElem = $('#'+cloneId).clone();

    //remove the button before adding the input
    //we don't want a button on each element being appended
    cloneElem.find('#'+this.id).remove();

    //clear out the value for the element being appended
    //so the new element has a blank value
    cloneElem.find('input[type=text]').attr("value", "");

    // should we attach an auto complete based on the input
    if (this.id == 'additional_based_near_submit') {
      cloneElem.find('input[type=text]').autocomplete(cities_autocomplete_opts);
    }
    else if ( (index = $.inArray(this.id, autocomplete_vocab.add_btn_id)) != -1 ) {
      cloneElem.find('input[type=text]').autocomplete(get_autocomplete_opts(autocomplete_vocab.url_var[index]));
    }

    $('#'+newId).append(cloneElem);
    cloneElem.find('input[type=text]').focus();
    return false;
  });

  $('.icon-plus').click(function() {
    //this.id format: "expand_id:NNNNNNNNNN"
    var a = this.id.split("expand_id_");
    if (a.length > 1)
    {
      docId = a[1]
      $("#detail_id_"+docId).toggle();
      if( $("#detail_id_"+docId).is(":hidden") ) {
        $("#expand_id_"+docId).attr("class", "icon-plus");
      }
      else {
        $("#expand_id_"+docId).attr("class", "icon-minus");
      }
    }
  })

});
<|endoftext|>