/* Minification failed. Returning unminified contents.
(5219,7-15): run-time error JS1004: Expected ';': function
(5231,28-33): run-time error JS1004: Expected ';': fetch
(5232,26-34): run-time error JS1004: Expected ';': response
(5247,35-36): run-time error JS1014: Invalid character: `
(5247,41-42): run-time error JS1193: Expected ',' or ')': :
(5250,51-59): run-time error JS1193: Expected ',' or ')': function
(5250,61-62): run-time error JS1195: Expected expression: )
(5250,63-64): run-time error JS1004: Expected ';': {
(5257,40-45): run-time error JS1004: Expected ';': fetch
(5258,38-46): run-time error JS1004: Expected ';': response
(5274,10-11): run-time error JS1195: Expected expression: ,
(5278,5-6): run-time error JS1002: Syntax error: }
(5283,47-48): run-time error JS1004: Expected ';': {
(5331,16-17): run-time error JS1014: Invalid character: `
(5331,18-19): run-time error JS1004: Expected ';': {
(5331,34-35): run-time error JS1004: Expected ';': $
(5331,75-76): run-time error JS1014: Invalid character: `
(5332,7-11): run-time error JS1034: Unmatched 'else'; no 'if' defined: else
(15938,44-45): run-time error JS1195: Expected expression: >
(15943,53-54): run-time error JS1195: Expected expression: >
(15944,62-63): run-time error JS1195: Expected expression: >
(15950,9-10): run-time error JS1002: Syntax error: }
(15954,22-23): run-time error JS1002: Syntax error: }
(15957,1-14): run-time error JS1197: Too many errors. The file might not be a JavaScript file: classicSmiles
 */
/* NUGET: BEGIN LICENSE TEXT
 *
 * Microsoft grants you the right to use these script files for the sole
 * purpose of either: (i) interacting through your browser with the Microsoft
 * website or online service, subject to the applicable licensing or use
 * terms; or (ii) using the files as included with a Microsoft product subject
 * to that product's license terms. Microsoft reserves all other rights to the
 * files not expressly granted by Microsoft, whether by implication, estoppel
 * or otherwise. Insofar as a script file is dual licensed under GPL,
 * Microsoft neither took the code under GPL nor distributes it thereunder but
 * under the terms set out in this paragraph. All notices and licenses
 * below are for informational purposes only.
 *
 * NUGET: END LICENSE TEXT */

/**
* bootstrap.js v3.0.0 by @fat and @mdo
* Copyright 2013 Twitter Inc.
* http://www.apache.org/licenses/LICENSE-2.0
*/
if (!jQuery) { throw new Error("Bootstrap requires jQuery") }

/* ========================================================================
 * Bootstrap: transition.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#transitions
 * ========================================================================
 * Copyright 2013 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
  // ============================================================

  function transitionEnd() {
    var el = document.createElement('bootstrap')

    var transEndEventNames = {
      'WebkitTransition' : 'webkitTransitionEnd'
    , 'MozTransition'    : 'transitionend'
    , 'OTransition'      : 'oTransitionEnd otransitionend'
    , 'transition'       : 'transitionend'
    }

    for (var name in transEndEventNames) {
      if (el.style[name] !== undefined) {
        return { end: transEndEventNames[name] }
      }
    }
  }

  // http://blog.alexmaccaw.com/css-transitions
  $.fn.emulateTransitionEnd = function (duration) {
    var called = false, $el = this
    $(this).one($.support.transition.end, function () { called = true })
    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
    setTimeout(callback, duration)
    return this
  }

  $(function () {
    $.support.transition = transitionEnd()
  })

}(window.jQuery);

/* ========================================================================
 * Bootstrap: alert.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#alerts
 * ========================================================================
 * Copyright 2013 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // ALERT CLASS DEFINITION
  // ======================

  var dismiss = '[data-dismiss="alert"]'
  var Alert   = function (el) {
    $(el).on('click', dismiss, this.close)
  }

  Alert.prototype.close = function (e) {
    var $this    = $(this)
    var selector = $this.attr('data-target')

    if (!selector) {
      selector = $this.attr('href')
      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
    }

    var $parent = $(selector)

    if (e) e.preventDefault()

    if (!$parent.length) {
      $parent = $this.hasClass('alert') ? $this : $this.parent()
    }

    $parent.trigger(e = $.Event('close.bs.alert'))

    if (e.isDefaultPrevented()) return

    $parent.removeClass('in')

    function removeElement() {
      $parent.trigger('closed.bs.alert').remove()
    }

    $.support.transition && $parent.hasClass('fade') ?
      $parent
        .one($.support.transition.end, removeElement)
        .emulateTransitionEnd(150) :
      removeElement()
  }


  // ALERT PLUGIN DEFINITION
  // =======================

  var old = $.fn.alert

  $.fn.alert = function (option) {
    return this.each(function () {
      var $this = $(this)
      var data  = $this.data('bs.alert')

      if (!data) $this.data('bs.alert', (data = new Alert(this)))
      if (typeof option == 'string') data[option].call($this)
    })
  }

  $.fn.alert.Constructor = Alert


  // ALERT NO CONFLICT
  // =================

  $.fn.alert.noConflict = function () {
    $.fn.alert = old
    return this
  }


  // ALERT DATA-API
  // ==============

  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)

}(window.jQuery);

/* ========================================================================
 * Bootstrap: button.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#buttons
 * ========================================================================
 * Copyright 2013 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // BUTTON PUBLIC CLASS DEFINITION
  // ==============================

  var Button = function (element, options) {
    this.$element = $(element)
    this.options  = $.extend({}, Button.DEFAULTS, options)
  }

  Button.DEFAULTS = {
    loadingText: 'loading...'
  }

  Button.prototype.setState = function (state) {
    var d    = 'disabled'
    var $el  = this.$element
    var val  = $el.is('input') ? 'val' : 'html'
    var data = $el.data()

    state = state + 'Text'

    if (!data.resetText) $el.data('resetText', $el[val]())

    $el[val](data[state] || this.options[state])

    // push to event loop to allow forms to submit
    setTimeout(function () {
      state == 'loadingText' ?
        $el.addClass(d).attr(d, d) :
        $el.removeClass(d).removeAttr(d);
    }, 0)
  }

  Button.prototype.toggle = function () {
    var $parent = this.$element.closest('[data-toggle="buttons"]')

    if ($parent.length) {
      var $input = this.$element.find('input')
        .prop('checked', !this.$element.hasClass('active'))
        .trigger('change')
      if ($input.prop('type') === 'radio') $parent.find('.active').removeClass('active')
    }

    this.$element.toggleClass('active')
  }


  // BUTTON PLUGIN DEFINITION
  // ========================

  var old = $.fn.button

  $.fn.button = function (option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.button')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.button', (data = new Button(this, options)))

      if (option == 'toggle') data.toggle()
      else if (option) data.setState(option)
    })
  }

  $.fn.button.Constructor = Button


  // BUTTON NO CONFLICT
  // ==================

  $.fn.button.noConflict = function () {
    $.fn.button = old
    return this
  }


  // BUTTON DATA-API
  // ===============

  $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
    var $btn = $(e.target)
    if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
    $btn.button('toggle')
    e.preventDefault()
  })

}(window.jQuery);

/* ========================================================================
 * Bootstrap: carousel.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#carousel
 * ========================================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // CAROUSEL CLASS DEFINITION
  // =========================

  var Carousel = function (element, options) {
    this.$element    = $(element)
    this.$indicators = this.$element.find('.carousel-indicators')
    this.options     = options
    this.paused      =
    this.sliding     =
    this.interval    =
    this.$active     =
    this.$items      = null

    this.options.pause == 'hover' && this.$element
      .on('mouseenter', $.proxy(this.pause, this))
      .on('mouseleave', $.proxy(this.cycle, this))
  }

  Carousel.DEFAULTS = {
    interval: 5000
  , pause: 'hover'
  , wrap: true
  }

  Carousel.prototype.cycle =  function (e) {
    e || (this.paused = false)

    this.interval && clearInterval(this.interval)

    this.options.interval
      && !this.paused
      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))

    return this
  }

  Carousel.prototype.getActiveIndex = function () {
    this.$active = this.$element.find('.item.active')
    this.$items  = this.$active.parent().children()

    return this.$items.index(this.$active)
  }

  Carousel.prototype.to = function (pos) {
    var that        = this
    var activeIndex = this.getActiveIndex()

    if (pos > (this.$items.length - 1) || pos < 0) return

    if (this.sliding)       return this.$element.one('slid', function () { that.to(pos) })
    if (activeIndex == pos) return this.pause().cycle()

    return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
  }

  Carousel.prototype.pause = function (e) {
    e || (this.paused = true)

    if (this.$element.find('.next, .prev').length && $.support.transition.end) {
      this.$element.trigger($.support.transition.end)
      this.cycle(true)
    }

    this.interval = clearInterval(this.interval)

    return this
  }

  Carousel.prototype.next = function () {
    if (this.sliding) return
    return this.slide('next')
  }

  Carousel.prototype.prev = function () {
    if (this.sliding) return
    return this.slide('prev')
  }

  Carousel.prototype.slide = function (type, next) {
    var $active   = this.$element.find('.item.active')
    var $next     = next || $active[type]()
    var isCycling = this.interval
    var direction = type == 'next' ? 'left' : 'right'
    var fallback  = type == 'next' ? 'first' : 'last'
    var that      = this

    if (!$next.length) {
      if (!this.options.wrap) return
      $next = this.$element.find('.item')[fallback]()
    }

    this.sliding = true

    isCycling && this.pause()

    var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })

    if ($next.hasClass('active')) return

    if (this.$indicators.length) {
      this.$indicators.find('.active').removeClass('active')
      this.$element.one('slid', function () {
        var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
        $nextIndicator && $nextIndicator.addClass('active')
      })
    }

    if ($.support.transition && this.$element.hasClass('slide')) {
      this.$element.trigger(e)
      if (e.isDefaultPrevented()) return
      $next.addClass(type)
      $next[0].offsetWidth // force reflow
      $active.addClass(direction)
      $next.addClass(direction)
      $active
        .one($.support.transition.end, function () {
          $next.removeClass([type, direction].join(' ')).addClass('active')
          $active.removeClass(['active', direction].join(' '))
          that.sliding = false
          setTimeout(function () { that.$element.trigger('slid') }, 0)
        })
        .emulateTransitionEnd(600)
    } else {
      this.$element.trigger(e)
      if (e.isDefaultPrevented()) return
      $active.removeClass('active')
      $next.addClass('active')
      this.sliding = false
      this.$element.trigger('slid')
    }

    isCycling && this.cycle()

    return this
  }


  // CAROUSEL PLUGIN DEFINITION
  // ==========================

  var old = $.fn.carousel

  $.fn.carousel = function (option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.carousel')
      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
      var action  = typeof option == 'string' ? option : options.slide

      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
      if (typeof option == 'number') data.to(option)
      else if (action) data[action]()
      else if (options.interval) data.pause().cycle()
    })
  }

  $.fn.carousel.Constructor = Carousel


  // CAROUSEL NO CONFLICT
  // ====================

  $.fn.carousel.noConflict = function () {
    $.fn.carousel = old
    return this
  }


  // CAROUSEL DATA-API
  // =================

  $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
    var $this   = $(this), href
    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
    var options = $.extend({}, $target.data(), $this.data())
    var slideIndex = $this.attr('data-slide-to')
    if (slideIndex) options.interval = false

    $target.carousel(options)

    if (slideIndex = $this.attr('data-slide-to')) {
      $target.data('bs.carousel').to(slideIndex)
    }

    e.preventDefault()
  })

  $(window).on('load', function () {
    $('[data-ride="carousel"]').each(function () {
      var $carousel = $(this)
      $carousel.carousel($carousel.data())
    })
  })

}(window.jQuery);

/* ========================================================================
 * Bootstrap: collapse.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#collapse
 * ========================================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // COLLAPSE PUBLIC CLASS DEFINITION
  // ================================

  var Collapse = function (element, options) {
    this.$element      = $(element)
    this.options       = $.extend({}, Collapse.DEFAULTS, options)
    this.transitioning = null

    if (this.options.parent) this.$parent = $(this.options.parent)
    if (this.options.toggle) this.toggle()
  }

  Collapse.DEFAULTS = {
    toggle: true
  }

  Collapse.prototype.dimension = function () {
    var hasWidth = this.$element.hasClass('width')
    return hasWidth ? 'width' : 'height'
  }

  Collapse.prototype.show = function () {
    if (this.transitioning || this.$element.hasClass('in')) return

    var startEvent = $.Event('show.bs.collapse')
    this.$element.trigger(startEvent)
    if (startEvent.isDefaultPrevented()) return

    var actives = this.$parent && this.$parent.find('> div > .in')

    if (actives && actives.length) {
      var hasData = actives.data('bs.collapse')
      if (hasData && hasData.transitioning) return
      actives.collapse('hide')
      hasData || actives.data('bs.collapse', null)
    }

    var dimension = this.dimension()

    this.$element
      .removeClass('collapse')
      .addClass('collapsing')
      [dimension](0)

    this.transitioning = 1

    var complete = function () {
      this.$element
        .removeClass('collapsing')
        .addClass('in')
        [dimension]('auto')
      this.transitioning = 0
      this.$element.trigger('shown.bs.collapse')
    }

    if (!$.support.transition) return complete.call(this)

    var scrollSize = $.camelCase(['scroll', dimension].join('-'))

    this.$element
      .one($.support.transition.end, $.proxy(complete, this))
      .emulateTransitionEnd(350)
      [dimension](this.$element[0][scrollSize])
  }

  Collapse.prototype.hide = function () {
    if (this.transitioning || !this.$element.hasClass('in')) return

    var startEvent = $.Event('hide.bs.collapse')
    this.$element.trigger(startEvent)
    if (startEvent.isDefaultPrevented()) return

    var dimension = this.dimension()

    this.$element
      [dimension](this.$element[dimension]())
      [0].offsetHeight

    this.$element
      .addClass('collapsing')
      .removeClass('collapse')
      .removeClass('in')

    this.transitioning = 1

    var complete = function () {
      this.transitioning = 0
      this.$element
        .trigger('hidden.bs.collapse')
        .removeClass('collapsing')
        .addClass('collapse')
    }

    if (!$.support.transition) return complete.call(this)

    this.$element
      [dimension](0)
      .one($.support.transition.end, $.proxy(complete, this))
      .emulateTransitionEnd(350)
  }

  Collapse.prototype.toggle = function () {
    this[this.$element.hasClass('in') ? 'hide' : 'show']()
  }


  // COLLAPSE PLUGIN DEFINITION
  // ==========================

  var old = $.fn.collapse

  $.fn.collapse = function (option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.collapse')
      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)

      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.collapse.Constructor = Collapse


  // COLLAPSE NO CONFLICT
  // ====================

  $.fn.collapse.noConflict = function () {
    $.fn.collapse = old
    return this
  }


  // COLLAPSE DATA-API
  // =================

  $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
    var $this   = $(this), href
    var target  = $this.attr('data-target')
        || e.preventDefault()
        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
    var $target = $(target)
    var data    = $target.data('bs.collapse')
    var option  = data ? 'toggle' : $this.data()
    var parent  = $this.attr('data-parent')
    var $parent = parent && $(parent)

    if (!data || !data.transitioning) {
      if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
    }

    $target.collapse(option)
  })

}(window.jQuery);

/* ========================================================================
 * Bootstrap: dropdown.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#dropdowns
 * ========================================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // DROPDOWN CLASS DEFINITION
  // =========================

  var backdrop = '.dropdown-backdrop'
  var toggle   = '[data-toggle=dropdown]'
  var Dropdown = function (element) {
    var $el = $(element).on('click.bs.dropdown', this.toggle)
  }

  Dropdown.prototype.toggle = function (e) {
    var $this = $(this)

    if ($this.is('.disabled, :disabled')) return

    var $parent  = getParent($this)
    var isActive = $parent.hasClass('open')

    clearMenus()

    if (!isActive) {
      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
        // if mobile we we use a backdrop because click events don't delegate
        $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
      }

      $parent.trigger(e = $.Event('show.bs.dropdown'))

      if (e.isDefaultPrevented()) return

      $parent
        .toggleClass('open')
        .trigger('shown.bs.dropdown')

      $this.focus()
    }

    return false
  }

  Dropdown.prototype.keydown = function (e) {
    if (!/(38|40|27)/.test(e.keyCode)) return

    var $this = $(this)

    e.preventDefault()
    e.stopPropagation()

    if ($this.is('.disabled, :disabled')) return

    var $parent  = getParent($this)
    var isActive = $parent.hasClass('open')

    if (!isActive || (isActive && e.keyCode == 27)) {
      if (e.which == 27) $parent.find(toggle).focus()
      return $this.click()
    }

    var $items = $('[role=menu] li:not(.divider):visible a', $parent)

    if (!$items.length) return

    var index = $items.index($items.filter(':focus'))

    if (e.keyCode == 38 && index > 0)                 index--                        // up
    if (e.keyCode == 40 && index < $items.length - 1) index++                        // down
    if (!~index)                                      index=0

    $items.eq(index).focus()
  }

  function clearMenus() {
    $(backdrop).remove()
    $(toggle).each(function (e) {
      var $parent = getParent($(this))
      if (!$parent.hasClass('open')) return
      $parent.trigger(e = $.Event('hide.bs.dropdown'))
      if (e.isDefaultPrevented()) return
      $parent.removeClass('open').trigger('hidden.bs.dropdown')
    })
  }

  function getParent($this) {
    var selector = $this.attr('data-target')

    if (!selector) {
      selector = $this.attr('href')
      selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
    }

    var $parent = selector && $(selector)

    return $parent && $parent.length ? $parent : $this.parent()
  }


  // DROPDOWN PLUGIN DEFINITION
  // ==========================

  var old = $.fn.dropdown

  $.fn.dropdown = function (option) {
    return this.each(function () {
      var $this = $(this)
      var data  = $this.data('dropdown')

      if (!data) $this.data('dropdown', (data = new Dropdown(this)))
      if (typeof option == 'string') data[option].call($this)
    })
  }

  $.fn.dropdown.Constructor = Dropdown


  // DROPDOWN NO CONFLICT
  // ====================

  $.fn.dropdown.noConflict = function () {
    $.fn.dropdown = old
    return this
  }


  // APPLY TO STANDARD DROPDOWN ELEMENTS
  // ===================================

  $(document)
    .on('click.bs.dropdown.data-api', clearMenus)
    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
    .on('click.bs.dropdown.data-api'  , toggle, Dropdown.prototype.toggle)
    .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)

}(window.jQuery);

/* ========================================================================
 * Bootstrap: modal.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#modals
 * ========================================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // MODAL CLASS DEFINITION
  // ======================

  var Modal = function (element, options) {
    this.options   = options
    this.$element  = $(element)
    this.$backdrop =
    this.isShown   = null

    if (this.options.remote) this.$element.load(this.options.remote)
  }

  Modal.DEFAULTS = {
      backdrop: true
    , keyboard: true
    , show: true
  }

  Modal.prototype.toggle = function (_relatedTarget) {
    return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
  }

  Modal.prototype.show = function (_relatedTarget) {
    var that = this
    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })

    this.$element.trigger(e)

    if (this.isShown || e.isDefaultPrevented()) return

    this.isShown = true

    this.escape()

    this.$element.on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))

    this.backdrop(function () {
      var transition = $.support.transition && that.$element.hasClass('fade')

      if (!that.$element.parent().length) {
        that.$element.appendTo(document.body) // don't move modals dom position
      }

      that.$element.show()

      if (transition) {
        that.$element[0].offsetWidth // force reflow
      }

      that.$element
        .addClass('in')
        .attr('aria-hidden', false)

      that.enforceFocus()

      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })

      transition ?
        that.$element.find('.modal-dialog') // wait for modal to slide in
          .one($.support.transition.end, function () {
            that.$element.focus().trigger(e)
          })
          .emulateTransitionEnd(300) :
        that.$element.focus().trigger(e)
    })
  }

  Modal.prototype.hide = function (e) {
    if (e) e.preventDefault()

    e = $.Event('hide.bs.modal')

    this.$element.trigger(e)

    if (!this.isShown || e.isDefaultPrevented()) return

    this.isShown = false

    this.escape()

    $(document).off('focusin.bs.modal')

    this.$element
      .removeClass('in')
      .attr('aria-hidden', true)
      .off('click.dismiss.modal')

    $.support.transition && this.$element.hasClass('fade') ?
      this.$element
        .one($.support.transition.end, $.proxy(this.hideModal, this))
        .emulateTransitionEnd(300) :
      this.hideModal()
  }

  Modal.prototype.enforceFocus = function () {
    $(document)
      .off('focusin.bs.modal') // guard against infinite focus loop
      .on('focusin.bs.modal', $.proxy(function (e) {
        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
          this.$element.focus()
        }
      }, this))
  }

  Modal.prototype.escape = function () {
    if (this.isShown && this.options.keyboard) {
      this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
        e.which == 27 && this.hide()
      }, this))
    } else if (!this.isShown) {
      this.$element.off('keyup.dismiss.bs.modal')
    }
  }

  Modal.prototype.hideModal = function () {
    var that = this
    this.$element.hide()
    this.backdrop(function () {
      that.removeBackdrop()
      that.$element.trigger('hidden.bs.modal')
    })
  }

  Modal.prototype.removeBackdrop = function () {
    this.$backdrop && this.$backdrop.remove()
    this.$backdrop = null
  }

  Modal.prototype.backdrop = function (callback) {
    var that    = this
    var animate = this.$element.hasClass('fade') ? 'fade' : ''

    if (this.isShown && this.options.backdrop) {
      var doAnimate = $.support.transition && animate

      this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
        .appendTo(document.body)

      this.$element.on('click.dismiss.modal', $.proxy(function (e) {
        if (e.target !== e.currentTarget) return
        this.options.backdrop == 'static'
          ? this.$element[0].focus.call(this.$element[0])
          : this.hide.call(this)
      }, this))

      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow

      this.$backdrop.addClass('in')

      if (!callback) return

      doAnimate ?
        this.$backdrop
          .one($.support.transition.end, callback)
          .emulateTransitionEnd(150) :
        callback()

    } else if (!this.isShown && this.$backdrop) {
      this.$backdrop.removeClass('in')

      $.support.transition && this.$element.hasClass('fade')?
        this.$backdrop
          .one($.support.transition.end, callback)
          .emulateTransitionEnd(150) :
        callback()

    } else if (callback) {
      callback()
    }
  }


  // MODAL PLUGIN DEFINITION
  // =======================

  var old = $.fn.modal

  $.fn.modal = function (option, _relatedTarget) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.modal')
      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)

      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
      if (typeof option == 'string') data[option](_relatedTarget)
      else if (options.show) data.show(_relatedTarget)
    })
  }

  $.fn.modal.Constructor = Modal


  // MODAL NO CONFLICT
  // =================

  $.fn.modal.noConflict = function () {
    $.fn.modal = old
    return this
  }


  // MODAL DATA-API
  // ==============

  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
    var $this   = $(this)
    var href    = $this.attr('href')
    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
    var option  = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())

    e.preventDefault()

    $target
      .modal(option, this)
      .one('hide', function () {
        $this.is(':visible') && $this.focus()
      })
  })

  $(document)
    .on('show.bs.modal',  '.modal', function () { $(document.body).addClass('modal-open') })
    .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })

}(window.jQuery);

/* ========================================================================
 * Bootstrap: tooltip.js v3.3.7
 * http://getbootstrap.com/javascript/#tooltip
 * Inspired by the original jQuery.tipsy by Jason Frame
 * ========================================================================
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */


+function ($) {
    'use strict';

    // TOOLTIP PUBLIC CLASS DEFINITION
    // ===============================

    var Tooltip = function (element, options) {
        this.type = null
        this.options = null
        this.enabled = null
        this.timeout = null
        this.hoverState = null
        this.$element = null
        this.inState = null

        this.init('tooltip', element, options)
    }

    Tooltip.VERSION = '3.3.7'

    Tooltip.TRANSITION_DURATION = 150

    Tooltip.DEFAULTS = {
        animation: true,
        placement: 'top',
        selector: false,
        template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
        trigger: 'hover focus',
        title: '',
        delay: 0,
        html: false,
        container: false,
        viewport: {
            selector: 'body',
            padding: 0
        }
    }

    Tooltip.prototype.init = function (type, element, options) {
        this.enabled = true
        this.type = type
        this.$element = $(element)
        this.options = this.getOptions(options)
        this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
        this.inState = { click: false, hover: false, focus: false }

        if (this.$element[0] instanceof document.constructor && !this.options.selector) {
            throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
        }

        var triggers = this.options.trigger.split(' ')

        for (var i = triggers.length; i--;) {
            var trigger = triggers[i]

            if (trigger == 'click') {
                this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
            } else if (trigger != 'manual') {
                var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
                var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'

                this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
                this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
            }
        }

        this.options.selector ?
            (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
            this.fixTitle()
    }

    Tooltip.prototype.getDefaults = function () {
        return Tooltip.DEFAULTS
    }

    Tooltip.prototype.getOptions = function (options) {
        options = $.extend({}, this.getDefaults(), this.$element.data(), options)

        if (options.delay && typeof options.delay == 'number') {
            options.delay = {
                show: options.delay,
                hide: options.delay
            }
        }

        return options
    }

    Tooltip.prototype.getDelegateOptions = function () {
        var options = {}
        var defaults = this.getDefaults()

        this._options && $.each(this._options, function (key, value) {
            if (defaults[key] != value) options[key] = value
        })

        return options
    }

    Tooltip.prototype.enter = function (obj) {
        var self = obj instanceof this.constructor ?
            obj : $(obj.currentTarget).data('bs.' + this.type)

        if (!self) {
            self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
            $(obj.currentTarget).data('bs.' + this.type, self)
        }

        if (obj instanceof $.Event) {
            self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
        }

        if (self.tip().hasClass('in') || self.hoverState == 'in') {
            self.hoverState = 'in'
            return
        }

        clearTimeout(self.timeout)

        self.hoverState = 'in'

        if (!self.options.delay || !self.options.delay.show) return self.show()

        self.timeout = setTimeout(function () {
            if (self.hoverState == 'in') self.show()
        }, self.options.delay.show)
    }

    Tooltip.prototype.isInStateTrue = function () {
        for (var key in this.inState) {
            if (this.inState[key]) return true
        }

        return false
    }

    Tooltip.prototype.leave = function (obj) {
        var self = obj instanceof this.constructor ?
            obj : $(obj.currentTarget).data('bs.' + this.type)

        if (!self) {
            self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
            $(obj.currentTarget).data('bs.' + this.type, self)
        }

        if (obj instanceof $.Event) {
            self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
        }

        if (self.isInStateTrue()) return

        clearTimeout(self.timeout)

        self.hoverState = 'out'

        if (!self.options.delay || !self.options.delay.hide) return self.hide()

        self.timeout = setTimeout(function () {
            if (self.hoverState == 'out') self.hide()
        }, self.options.delay.hide)
    }

    Tooltip.prototype.show = function () {
        var e = $.Event('show.bs.' + this.type)

        if (this.hasContent() && this.enabled) {
            this.$element.trigger(e)

            var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
            if (e.isDefaultPrevented() || !inDom) return
            var that = this

            var $tip = this.tip()

            var tipId = this.getUID(this.type)

            this.setContent()
            $tip.attr('id', tipId)
            this.$element.attr('aria-describedby', tipId)

            if (this.options.animation) $tip.addClass('fade')

            var placement = typeof this.options.placement == 'function' ?
                this.options.placement.call(this, $tip[0], this.$element[0]) :
                this.options.placement

            var autoToken = /\s?auto?\s?/i
            var autoPlace = autoToken.test(placement)
            if (autoPlace) placement = placement.replace(autoToken, '') || 'top'

            $tip
                .detach()
                .css({ top: 0, left: 0, display: 'block' })
                .addClass(placement)
                .data('bs.' + this.type, this)

            this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
            this.$element.trigger('inserted.bs.' + this.type)

            var pos = this.getPosition()
            var actualWidth = $tip[0].offsetWidth
            var actualHeight = $tip[0].offsetHeight

            if (autoPlace) {
                var orgPlacement = placement
                var viewportDim = this.getPosition(this.$viewport)

                placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :
                    placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :
                        placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :
                            placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :
                                placement

                $tip
                    .removeClass(orgPlacement)
                    .addClass(placement)
            }

            var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)

            this.applyPlacement(calculatedOffset, placement)

            var complete = function () {
                var prevHoverState = that.hoverState
                that.$element.trigger('shown.bs.' + that.type)
                that.hoverState = null

                if (prevHoverState == 'out') that.leave(that)
            }

            $.support.transition && this.$tip.hasClass('fade') ?
                $tip
                    .one('bsTransitionEnd', complete)
                    .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
                complete()
        }
    }

    Tooltip.prototype.applyPlacement = function (offset, placement) {
        var $tip = this.tip()
        var width = $tip[0].offsetWidth
        var height = $tip[0].offsetHeight

        // manually read margins because getBoundingClientRect includes difference
        var marginTop = parseInt($tip.css('margin-top'), 10)
        var marginLeft = parseInt($tip.css('margin-left'), 10)

        // we must check for NaN for ie 8/9
        if (isNaN(marginTop)) marginTop = 0
        if (isNaN(marginLeft)) marginLeft = 0

        offset.top += marginTop
        offset.left += marginLeft

        // $.fn.offset doesn't round pixel values
        // so we use setOffset directly with our own function B-0
        $.offset.setOffset($tip[0], $.extend({
            using: function (props) {
                $tip.css({
                    top: Math.round(props.top),
                    left: Math.round(props.left)
                })
            }
        }, offset), 0)

        $tip.addClass('in')

        // check to see if placing tip in new offset caused the tip to resize itself
        var actualWidth = $tip[0].offsetWidth
        var actualHeight = $tip[0].offsetHeight

        if (placement == 'top' && actualHeight != height) {
            offset.top = offset.top + height - actualHeight
        }

        var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)

        if (delta.left) offset.left += delta.left
        else offset.top += delta.top

        var isVertical = /top|bottom/.test(placement)
        var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
        var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'

        $tip.offset(offset)
        this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
    }

    Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
        this.arrow()
            .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
            .css(isVertical ? 'top' : 'left', '')
    }

    Tooltip.prototype.setContent = function () {
        var $tip = this.tip()
        var title = this.getTitle()

        $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
        $tip.removeClass('fade in top bottom left right')
    }

    Tooltip.prototype.hide = function (callback) {
        var that = this
        var $tip = $(this.$tip)
        var e = $.Event('hide.bs.' + this.type)

        function complete() {
            if (that.hoverState != 'in') $tip.detach()
            if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.
                that.$element
                    .removeAttr('aria-describedby')
                    .trigger('hidden.bs.' + that.type)
            }
            callback && callback()
        }

        this.$element.trigger(e)

        if (e.isDefaultPrevented()) return

        $tip.removeClass('in')

        $.support.transition && $tip.hasClass('fade') ?
            $tip
                .one('bsTransitionEnd', complete)
                .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
            complete()

        this.hoverState = null

        return this
    }

    Tooltip.prototype.fixTitle = function () {
        var $e = this.$element
        if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
            $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
        }
    }

    Tooltip.prototype.hasContent = function () {
        return this.getTitle()
    }

    Tooltip.prototype.getPosition = function ($element) {
        $element = $element || this.$element

        var el = $element[0]
        var isBody = el.tagName == 'BODY'

        var elRect = el.getBoundingClientRect()
        if (elRect.width == null) {
            // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
            elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
        }
        var isSvg = window.SVGElement && el instanceof window.SVGElement
        // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.
        // See https://github.com/twbs/bootstrap/issues/20280
        var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())
        var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
        var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null

        return $.extend({}, elRect, scroll, outerDims, elOffset)
    }

    Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
        return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
            placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
                placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }

    }

    Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
        var delta = { top: 0, left: 0 }
        if (!this.$viewport) return delta

        var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
        var viewportDimensions = this.getPosition(this.$viewport)

        if (/right|left/.test(placement)) {
            var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
            var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
            if (topEdgeOffset < viewportDimensions.top) { // top overflow
                delta.top = viewportDimensions.top - topEdgeOffset
            } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
                delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
            }
        } else {
            var leftEdgeOffset = pos.left - viewportPadding
            var rightEdgeOffset = pos.left + viewportPadding + actualWidth
            if (leftEdgeOffset < viewportDimensions.left) { // left overflow
                delta.left = viewportDimensions.left - leftEdgeOffset
            } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
                delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
            }
        }

        return delta
    }

    Tooltip.prototype.getTitle = function () {
        var title
        var $e = this.$element
        var o = this.options

        title = $e.attr('data-original-title')
            || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)

        return title
    }

    Tooltip.prototype.getUID = function (prefix) {
        do prefix += ~~(Math.random() * 1000000)
        while (document.getElementById(prefix))
        return prefix
    }

    Tooltip.prototype.tip = function () {
        if (!this.$tip) {
            this.$tip = $(this.options.template)
            if (this.$tip.length != 1) {
                throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
            }
        }
        return this.$tip
    }

    Tooltip.prototype.arrow = function () {
        return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
    }

    Tooltip.prototype.enable = function () {
        this.enabled = true
    }

    Tooltip.prototype.disable = function () {
        this.enabled = false
    }

    Tooltip.prototype.toggleEnabled = function () {
        this.enabled = !this.enabled
    }

    Tooltip.prototype.toggle = function (e) {
        var self = this
        if (e) {
            self = $(e.currentTarget).data('bs.' + this.type)
            if (!self) {
                self = new this.constructor(e.currentTarget, this.getDelegateOptions())
                $(e.currentTarget).data('bs.' + this.type, self)
            }
        }

        if (e) {
            self.inState.click = !self.inState.click
            if (self.isInStateTrue()) self.enter(self)
            else self.leave(self)
        } else {
            self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
        }
    }

    Tooltip.prototype.destroy = function () {
        var that = this
        clearTimeout(this.timeout)
        this.hide(function () {
            that.$element.off('.' + that.type).removeData('bs.' + that.type)
            if (that.$tip) {
                that.$tip.detach()
            }
            that.$tip = null
            that.$arrow = null
            that.$viewport = null
            that.$element = null
        })
    }


    // TOOLTIP PLUGIN DEFINITION
    // =========================

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this)
            var data = $this.data('bs.tooltip')
            var options = typeof option == 'object' && option

            if (!data && /destroy|hide/.test(option)) return
            if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
            if (typeof option == 'string') data[option]()
        })
    }

    var old = $.fn.tooltip

    $.fn.tooltip = Plugin
    $.fn.tooltip.Constructor = Tooltip


    // TOOLTIP NO CONFLICT
    // ===================

    $.fn.tooltip.noConflict = function () {
        $.fn.tooltip = old
        return this
    }

}(jQuery);

/* ========================================================================
 * Bootstrap: popover.js v3.3.7
 * http://getbootstrap.com/javascript/#popovers
 * ========================================================================
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */


+function ($) {
    'use strict';

    // POPOVER PUBLIC CLASS DEFINITION
    // ===============================

    var Popover = function (element, options) {
        this.init('popover', element, options)
    }

    if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')

    Popover.VERSION = '3.3.7'

    Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
        placement: 'right',
        trigger: 'click',
        content: '',
        template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
    })


    // NOTE: POPOVER EXTENDS tooltip.js
    // ================================

    Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)

    Popover.prototype.constructor = Popover

    Popover.prototype.getDefaults = function () {
        return Popover.DEFAULTS
    }

    Popover.prototype.setContent = function () {
        var $tip = this.tip()
        var title = this.getTitle()
        var content = this.getContent()

        $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
        $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
            this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
        ](content)

        $tip.removeClass('fade top bottom left right in')

        // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
        // this manually by checking the contents.
        if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
    }

    Popover.prototype.hasContent = function () {
        return this.getTitle() || this.getContent()
    }

    Popover.prototype.getContent = function () {
        var $e = this.$element
        var o = this.options

        return $e.attr('data-content')
            || (typeof o.content == 'function' ?
                o.content.call($e[0]) :
                o.content)
    }

    Popover.prototype.arrow = function () {
        return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
    }


    // POPOVER PLUGIN DEFINITION
    // =========================

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this)
            var data = $this.data('bs.popover')
            var options = typeof option == 'object' && option

            if (!data && /destroy|hide/.test(option)) return
            if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
            if (typeof option == 'string') data[option]()
        })
    }

    var old = $.fn.popover

    $.fn.popover = Plugin
    $.fn.popover.Constructor = Popover


    // POPOVER NO CONFLICT
    // ===================

    $.fn.popover.noConflict = function () {
        $.fn.popover = old
        return this
    }

}(jQuery);
  
/* ========================================================================
 * Bootstrap: scrollspy.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#scrollspy
 * ========================================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // SCROLLSPY CLASS DEFINITION
  // ==========================

  function ScrollSpy(element, options) {
    var href
    var process  = $.proxy(this.process, this)

    this.$element       = $(element).is('body') ? $(window) : $(element)
    this.$body          = $('body')
    this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
    this.selector       = (this.options.target
      || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
      || '') + ' .nav li > a'
    this.offsets        = $([])
    this.targets        = $([])
    this.activeTarget   = null

    this.refresh()
    this.process()
  }

  ScrollSpy.DEFAULTS = {
    offset: 10
  }

  ScrollSpy.prototype.refresh = function () {
    var offsetMethod = this.$element[0] == window ? 'offset' : 'position'

    this.offsets = $([])
    this.targets = $([])

    var self     = this
    var $targets = this.$body
      .find(this.selector)
      .map(function () {
        var $el   = $(this)
        var href  = $el.data('target') || $el.attr('href')
        var $href = /^#\w/.test(href) && $(href)

        return ($href
          && $href.length
          && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
      })
      .sort(function (a, b) { return a[0] - b[0] })
      .each(function () {
        self.offsets.push(this[0])
        self.targets.push(this[1])
      })
  }

  ScrollSpy.prototype.process = function () {
    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
    var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
    var maxScroll    = scrollHeight - this.$scrollElement.height()
    var offsets      = this.offsets
    var targets      = this.targets
    var activeTarget = this.activeTarget
    var i

    if (scrollTop >= maxScroll) {
      return activeTarget != (i = targets.last()[0]) && this.activate(i)
    }

    for (i = offsets.length; i--;) {
      activeTarget != targets[i]
        && scrollTop >= offsets[i]
        && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
        && this.activate( targets[i] )
    }
  }

  ScrollSpy.prototype.activate = function (target) {
    this.activeTarget = target

    $(this.selector)
      .parents('.active')
      .removeClass('active')

    var selector = this.selector
      + '[data-target="' + target + '"],'
      + this.selector + '[href="' + target + '"]'

    var active = $(selector)
      .parents('li')
      .addClass('active')

    if (active.parent('.dropdown-menu').length)  {
      active = active
        .closest('li.dropdown')
        .addClass('active')
    }

    active.trigger('activate')
  }


  // SCROLLSPY PLUGIN DEFINITION
  // ===========================

  var old = $.fn.scrollspy

  $.fn.scrollspy = function (option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.scrollspy')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.scrollspy.Constructor = ScrollSpy


  // SCROLLSPY NO CONFLICT
  // =====================

  $.fn.scrollspy.noConflict = function () {
    $.fn.scrollspy = old
    return this
  }


  // SCROLLSPY DATA-API
  // ==================

  $(window).on('load', function () {
    $('[data-spy="scroll"]').each(function () {
      var $spy = $(this)
      $spy.scrollspy($spy.data())
    })
  })

}(window.jQuery);

/* ========================================================================
 * Bootstrap: tab.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#tabs
 * ========================================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // TAB CLASS DEFINITION
  // ====================

  var Tab = function (element) {
    this.element = $(element)
  }

  Tab.prototype.show = function () {
    var $this    = this.element
    var $ul      = $this.closest('ul:not(.dropdown-menu)')
    var selector = $this.attr('data-target')

    if (!selector) {
      selector = $this.attr('href')
      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
    }

    if ($this.parent('li').hasClass('active')) return

    var previous = $ul.find('.active:last a')[0]
    var e        = $.Event('show.bs.tab', {
      relatedTarget: previous
    })

    $this.trigger(e)

    if (e.isDefaultPrevented()) return

    var $target = $(selector)

    this.activate($this.parent('li'), $ul)
    this.activate($target, $target.parent(), function () {
      $this.trigger({
        type: 'shown.bs.tab'
      , relatedTarget: previous
      })
    })
  }

  Tab.prototype.activate = function (element, container, callback) {
    var $active    = container.find('> .active')
    var transition = callback
      && $.support.transition
      && $active.hasClass('fade')

    function next() {
      $active
        .removeClass('active')
        .find('> .dropdown-menu > .active')
        .removeClass('active')

      element.addClass('active')

      if (transition) {
        element[0].offsetWidth // reflow for transition
        element.addClass('in')
      } else {
        element.removeClass('fade')
      }

      if (element.parent('.dropdown-menu')) {
        element.closest('li.dropdown').addClass('active')
      }

      callback && callback()
    }

    transition ?
      $active
        .one($.support.transition.end, next)
        .emulateTransitionEnd(150) :
      next()

    $active.removeClass('in')
  }


  // TAB PLUGIN DEFINITION
  // =====================

  var old = $.fn.tab

  $.fn.tab = function ( option ) {
    return this.each(function () {
      var $this = $(this)
      var data  = $this.data('bs.tab')

      if (!data) $this.data('bs.tab', (data = new Tab(this)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.tab.Constructor = Tab


  // TAB NO CONFLICT
  // ===============

  $.fn.tab.noConflict = function () {
    $.fn.tab = old
    return this
  }


  // TAB DATA-API
  // ============

  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
    e.preventDefault()
    $(this).tab('show')
  })

}(window.jQuery);

/* ========================================================================
 * Bootstrap: affix.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#affix
 * ========================================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */


+function ($) { "use strict";

  // AFFIX CLASS DEFINITION
  // ======================

  var Affix = function (element, options) {
    this.options = $.extend({}, Affix.DEFAULTS, options)
    this.$window = $(window)
      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))

    this.$element = $(element)
    this.affixed  =
    this.unpin    = null

    this.checkPosition()
  }

  Affix.RESET = 'affix affix-top affix-bottom'

  Affix.DEFAULTS = {
    offset: 0
  }

  Affix.prototype.checkPositionWithEventLoop = function () {
    setTimeout($.proxy(this.checkPosition, this), 1)
  }

  Affix.prototype.checkPosition = function () {
    if (!this.$element.is(':visible')) return

    var scrollHeight = $(document).height()
    var scrollTop    = this.$window.scrollTop()
    var position     = this.$element.offset()
    var offset       = this.options.offset
    var offsetTop    = offset.top
    var offsetBottom = offset.bottom

    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
    if (typeof offsetTop == 'function')    offsetTop    = offset.top()
    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()

    var affix = this.unpin   != null && (scrollTop + this.unpin <= position.top) ? false :
                offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
                offsetTop    != null && (scrollTop <= offsetTop) ? 'top' : false

    if (this.affixed === affix) return
    if (this.unpin) this.$element.css('top', '')

    this.affixed = affix
    this.unpin   = affix == 'bottom' ? position.top - scrollTop : null

    this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : ''))

    if (affix == 'bottom') {
      this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })
    }
  }


  // AFFIX PLUGIN DEFINITION
  // =======================

  var old = $.fn.affix

  $.fn.affix = function (option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.affix')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.affix.Constructor = Affix


  // AFFIX NO CONFLICT
  // =================

  $.fn.affix.noConflict = function () {
    $.fn.affix = old
    return this
  }


  // AFFIX DATA-API
  // ==============

  $(window).on('load', function () {
    $('[data-spy="affix"]').each(function () {
      var $spy = $(this)
      var data = $spy.data()

      data.offset = data.offset || {}

      if (data.offsetBottom) data.offset.bottom = data.offsetBottom
      if (data.offsetTop)    data.offset.top    = data.offsetTop

      $spy.affix(data)
    })
  })

}(window.jQuery);
;
// Global ManiaExchange JS/CS framework configuration.
var Config = {
	jqueryUIDialogHeight: 170,
	jqueryUIDialogWidth: 380
};;
/*! Select2 4.0.12 | https://github.com/select2/select2/blob/master/LICENSE.md */
!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(d){var e=function(){if(d&&d.fn&&d.fn.select2&&d.fn.select2.amd)var e=d.fn.select2.amd;var t,n,i,h,o,s,f,g,m,v,y,_,r,a,w,l;function b(e,t){return r.call(e,t)}function c(e,t){var n,i,r,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["*"]||{};if(e){for(s=(e=e.split("/")).length-1,y.nodeIdCompat&&w.test(e[s])&&(e[s]=e[s].replace(w,"")),"."===e[0].charAt(0)&&h&&(e=h.slice(0,h.length-1).concat(e)),u=0;u<e.length;u++)if("."===(p=e[u]))e.splice(u,1),u-=1;else if(".."===p){if(0===u||1===u&&".."===e[2]||".."===e[u-1])continue;0<u&&(e.splice(u-1,2),u-=2)}e=e.join("/")}if((h||g)&&f){for(u=(n=e.split("/")).length;0<u;u-=1){if(i=n.slice(0,u).join("/"),h)for(d=h.length;0<d;d-=1)if(r=(r=f[h.slice(0,d).join("/")])&&r[i]){o=r,a=u;break}if(o)break;!l&&g&&g[i]&&(l=g[i],c=u)}!o&&l&&(o=l,a=c),o&&(n.splice(0,a,o),e=n.join("/"))}return e}function A(t,n){return function(){var e=a.call(arguments,0);return"string"!=typeof e[0]&&1===e.length&&e.push(null),s.apply(h,e.concat([t,n]))}}function x(t){return function(e){m[t]=e}}function S(e){if(b(v,e)){var t=v[e];delete v[e],_[e]=!0,o.apply(h,t)}if(!b(m,e)&&!b(_,e))throw new Error("No "+e);return m[e]}function u(e){var t,n=e?e.indexOf("!"):-1;return-1<n&&(t=e.substring(0,n),e=e.substring(n+1,e.length)),[t,e]}function D(e){return e?u(e):[]}return e&&e.requirejs||(e?n=e:e={},m={},v={},y={},_={},r=Object.prototype.hasOwnProperty,a=[].slice,w=/\.js$/,f=function(e,t){var n,i=u(e),r=i[0],o=t[1];return e=i[1],r&&(n=S(r=c(r,o))),r?e=n&&n.normalize?n.normalize(e,function(t){return function(e){return c(e,t)}}(o)):c(e,o):(r=(i=u(e=c(e,o)))[0],e=i[1],r&&(n=S(r))),{f:r?r+"!"+e:e,n:e,pr:r,p:n}},g={require:function(e){return A(e)},exports:function(e){var t=m[e];return void 0!==t?t:m[e]={}},module:function(e){return{id:e,uri:"",exports:m[e],config:function(e){return function(){return y&&y.config&&y.config[e]||{}}}(e)}}},o=function(e,t,n,i){var r,o,s,a,l,c,u,d=[],p=typeof n;if(c=D(i=i||e),"undefined"==p||"function"==p){for(t=!t.length&&n.length?["require","exports","module"]:t,l=0;l<t.length;l+=1)if("require"===(o=(a=f(t[l],c)).f))d[l]=g.require(e);else if("exports"===o)d[l]=g.exports(e),u=!0;else if("module"===o)r=d[l]=g.module(e);else if(b(m,o)||b(v,o)||b(_,o))d[l]=S(o);else{if(!a.p)throw new Error(e+" missing "+o);a.p.load(a.n,A(i,!0),x(o),{}),d[l]=m[o]}s=n?n.apply(m[e],d):void 0,e&&(r&&r.exports!==h&&r.exports!==m[e]?m[e]=r.exports:s===h&&u||(m[e]=s))}else e&&(m[e]=n)},t=n=s=function(e,t,n,i,r){if("string"==typeof e)return g[e]?g[e](t):S(f(e,D(t)).f);if(!e.splice){if((y=e).deps&&s(y.deps,y.callback),!t)return;t.splice?(e=t,t=n,n=null):e=h}return t=t||function(){},"function"==typeof n&&(n=i,i=r),i?o(h,e,t,n):setTimeout(function(){o(h,e,t,n)},4),s},s.config=function(e){return s(e)},t._defined=m,(i=function(e,t,n){if("string"!=typeof e)throw new Error("See almond README: incorrect module build, no module name");t.splice||(n=t,t=[]),b(m,e)||b(v,e)||(v[e]=[e,t,n])}).amd={jQuery:!0},e.requirejs=t,e.require=n,e.define=i),e.define("almond",function(){}),e.define("jquery",[],function(){var e=d||$;return null==e&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),e}),e.define("select2/utils",["jquery"],function(o){var r={};function u(e){var t=e.prototype,n=[];for(var i in t){"function"==typeof t[i]&&"constructor"!==i&&n.push(i)}return n}r.Extend=function(e,t){var n={}.hasOwnProperty;function i(){this.constructor=e}for(var r in t)n.call(t,r)&&(e[r]=t[r]);return i.prototype=t.prototype,e.prototype=new i,e.__super__=t.prototype,e},r.Decorate=function(i,r){var e=u(r),t=u(i);function o(){var e=Array.prototype.unshift,t=r.prototype.constructor.length,n=i.prototype.constructor;0<t&&(e.call(arguments,i.prototype.constructor),n=r.prototype.constructor),n.apply(this,arguments)}r.displayName=i.displayName,o.prototype=new function(){this.constructor=o};for(var n=0;n<t.length;n++){var s=t[n];o.prototype[s]=i.prototype[s]}function a(e){var t=function(){};e in o.prototype&&(t=o.prototype[e]);var n=r.prototype[e];return function(){return Array.prototype.unshift.call(arguments,t),n.apply(this,arguments)}}for(var l=0;l<e.length;l++){var c=e[l];o.prototype[c]=a(c)}return o};function e(){this.listeners={}}e.prototype.on=function(e,t){this.listeners=this.listeners||{},e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t]},e.prototype.trigger=function(e){var t=Array.prototype.slice,n=t.call(arguments,1);this.listeners=this.listeners||{},null==n&&(n=[]),0===n.length&&n.push({}),(n[0]._type=e)in this.listeners&&this.invoke(this.listeners[e],t.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},e.prototype.invoke=function(e,t){for(var n=0,i=e.length;n<i;n++)e[n].apply(this,t)},r.Observable=e,r.generateChars=function(e){for(var t="",n=0;n<e;n++){t+=Math.floor(36*Math.random()).toString(36)}return t},r.bind=function(e,t){return function(){e.apply(t,arguments)}},r._convertData=function(e){for(var t in e){var n=t.split("-"),i=e;if(1!==n.length){for(var r=0;r<n.length;r++){var o=n[r];(o=o.substring(0,1).toLowerCase()+o.substring(1))in i||(i[o]={}),r==n.length-1&&(i[o]=e[t]),i=i[o]}delete e[t]}}return e},r.hasScroll=function(e,t){var n=o(t),i=t.style.overflowX,r=t.style.overflowY;return(i!==r||"hidden"!==r&&"visible"!==r)&&("scroll"===i||"scroll"===r||(n.innerHeight()<t.scrollHeight||n.innerWidth()<t.scrollWidth))},r.escapeMarkup=function(e){var t={"\\":"&#92;","&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#47;"};return"string"!=typeof e?e:String(e).replace(/[&<>"'\/\\]/g,function(e){return t[e]})},r.appendMany=function(e,t){if("1.7"===o.fn.jquery.substr(0,3)){var n=o();o.map(t,function(e){n=n.add(e)}),t=n}e.append(t)},r.__cache={};var n=0;return r.GetUniqueElementId=function(e){var t=e.getAttribute("data-select2-id");return null==t&&(e.id?(t=e.id,e.setAttribute("data-select2-id",t)):(e.setAttribute("data-select2-id",++n),t=n.toString())),t},r.StoreData=function(e,t,n){var i=r.GetUniqueElementId(e);r.__cache[i]||(r.__cache[i]={}),r.__cache[i][t]=n},r.GetData=function(e,t){var n=r.GetUniqueElementId(e);return t?r.__cache[n]&&null!=r.__cache[n][t]?r.__cache[n][t]:o(e).data(t):r.__cache[n]},r.RemoveData=function(e){var t=r.GetUniqueElementId(e);null!=r.__cache[t]&&delete r.__cache[t],e.removeAttribute("data-select2-id")},r}),e.define("select2/results",["jquery","./utils"],function(h,f){function i(e,t,n){this.$element=e,this.data=n,this.options=t,i.__super__.constructor.call(this)}return f.Extend(i,f.Observable),i.prototype.render=function(){var e=h('<ul class="select2-results__options" role="listbox"></ul>');return this.options.get("multiple")&&e.attr("aria-multiselectable","true"),this.$results=e},i.prototype.clear=function(){this.$results.empty()},i.prototype.displayMessage=function(e){var t=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var n=h('<li role="alert" aria-live="assertive" class="select2-results__option"></li>'),i=this.options.get("translations").get(e.message);n.append(t(i(e.args))),n[0].className+=" select2-results__message",this.$results.append(n)},i.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},i.prototype.append=function(e){this.hideLoading();var t=[];if(null!=e.results&&0!==e.results.length){e.results=this.sort(e.results);for(var n=0;n<e.results.length;n++){var i=e.results[n],r=this.option(i);t.push(r)}this.$results.append(t)}else 0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"})},i.prototype.position=function(e,t){t.find(".select2-results").append(e)},i.prototype.sort=function(e){return this.options.get("sorter")(e)},i.prototype.highlightFirstItem=function(){var e=this.$results.find(".select2-results__option[aria-selected]"),t=e.filter("[aria-selected=true]");0<t.length?t.first().trigger("mouseenter"):e.first().trigger("mouseenter"),this.ensureHighlightVisible()},i.prototype.setClasses=function(){var t=this;this.data.current(function(e){var i=h.map(e,function(e){return e.id.toString()});t.$results.find(".select2-results__option[aria-selected]").each(function(){var e=h(this),t=f.GetData(this,"data"),n=""+t.id;null!=t.element&&t.element.selected||null==t.element&&-1<h.inArray(n,i)?e.attr("aria-selected","true"):e.attr("aria-selected","false")})})},i.prototype.showLoading=function(e){this.hideLoading();var t={disabled:!0,loading:!0,text:this.options.get("translations").get("searching")(e)},n=this.option(t);n.className+=" loading-results",this.$results.prepend(n)},i.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},i.prototype.option=function(e){var t=document.createElement("li");t.className="select2-results__option";var n={role:"option","aria-selected":"false"},i=window.Element.prototype.matches||window.Element.prototype.msMatchesSelector||window.Element.prototype.webkitMatchesSelector;for(var r in(null!=e.element&&i.call(e.element,":disabled")||null==e.element&&e.disabled)&&(delete n["aria-selected"],n["aria-disabled"]="true"),null==e.id&&delete n["aria-selected"],null!=e._resultId&&(t.id=e._resultId),e.title&&(t.title=e.title),e.children&&(n.role="group",n["aria-label"]=e.text,delete n["aria-selected"]),n){var o=n[r];t.setAttribute(r,o)}if(e.children){var s=h(t),a=document.createElement("strong");a.className="select2-results__group";h(a);this.template(e,a);for(var l=[],c=0;c<e.children.length;c++){var u=e.children[c],d=this.option(u);l.push(d)}var p=h("<ul></ul>",{class:"select2-results__options select2-results__options--nested"});p.append(l),s.append(a),s.append(p)}else this.template(e,t);return f.StoreData(t,"data",e),t},i.prototype.bind=function(t,e){var l=this,n=t.id+"-results";this.$results.attr("id",n),t.on("results:all",function(e){l.clear(),l.append(e.data),t.isOpen()&&(l.setClasses(),l.highlightFirstItem())}),t.on("results:append",function(e){l.append(e.data),t.isOpen()&&l.setClasses()}),t.on("query",function(e){l.hideMessages(),l.showLoading(e)}),t.on("select",function(){t.isOpen()&&(l.setClasses(),l.options.get("scrollAfterSelect")&&l.highlightFirstItem())}),t.on("unselect",function(){t.isOpen()&&(l.setClasses(),l.options.get("scrollAfterSelect")&&l.highlightFirstItem())}),t.on("open",function(){l.$results.attr("aria-expanded","true"),l.$results.attr("aria-hidden","false"),l.setClasses(),l.ensureHighlightVisible()}),t.on("close",function(){l.$results.attr("aria-expanded","false"),l.$results.attr("aria-hidden","true"),l.$results.removeAttr("aria-activedescendant")}),t.on("results:toggle",function(){var e=l.getHighlightedResults();0!==e.length&&e.trigger("mouseup")}),t.on("results:select",function(){var e=l.getHighlightedResults();if(0!==e.length){var t=f.GetData(e[0],"data");"true"==e.attr("aria-selected")?l.trigger("close",{}):l.trigger("select",{data:t})}}),t.on("results:previous",function(){var e=l.getHighlightedResults(),t=l.$results.find("[aria-selected]"),n=t.index(e);if(!(n<=0)){var i=n-1;0===e.length&&(i=0);var r=t.eq(i);r.trigger("mouseenter");var o=l.$results.offset().top,s=r.offset().top,a=l.$results.scrollTop()+(s-o);0===i?l.$results.scrollTop(0):s-o<0&&l.$results.scrollTop(a)}}),t.on("results:next",function(){var e=l.getHighlightedResults(),t=l.$results.find("[aria-selected]"),n=t.index(e)+1;if(!(n>=t.length)){var i=t.eq(n);i.trigger("mouseenter");var r=l.$results.offset().top+l.$results.outerHeight(!1),o=i.offset().top+i.outerHeight(!1),s=l.$results.scrollTop()+o-r;0===n?l.$results.scrollTop(0):r<o&&l.$results.scrollTop(s)}}),t.on("results:focus",function(e){e.element.addClass("select2-results__option--highlighted")}),t.on("results:message",function(e){l.displayMessage(e)}),h.fn.mousewheel&&this.$results.on("mousewheel",function(e){var t=l.$results.scrollTop(),n=l.$results.get(0).scrollHeight-t+e.deltaY,i=0<e.deltaY&&t-e.deltaY<=0,r=e.deltaY<0&&n<=l.$results.height();i?(l.$results.scrollTop(0),e.preventDefault(),e.stopPropagation()):r&&(l.$results.scrollTop(l.$results.get(0).scrollHeight-l.$results.height()),e.preventDefault(),e.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(e){var t=h(this),n=f.GetData(this,"data");"true"!==t.attr("aria-selected")?l.trigger("select",{originalEvent:e,data:n}):l.options.get("multiple")?l.trigger("unselect",{originalEvent:e,data:n}):l.trigger("close",{})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(e){var t=f.GetData(this,"data");l.getHighlightedResults().removeClass("select2-results__option--highlighted"),l.trigger("results:focus",{data:t,element:h(this)})})},i.prototype.getHighlightedResults=function(){return this.$results.find(".select2-results__option--highlighted")},i.prototype.destroy=function(){this.$results.remove()},i.prototype.ensureHighlightVisible=function(){var e=this.getHighlightedResults();if(0!==e.length){var t=this.$results.find("[aria-selected]").index(e),n=this.$results.offset().top,i=e.offset().top,r=this.$results.scrollTop()+(i-n),o=i-n;r-=2*e.outerHeight(!1),t<=2?this.$results.scrollTop(0):(o>this.$results.outerHeight()||o<0)&&this.$results.scrollTop(r)}},i.prototype.template=function(e,t){var n=this.options.get("templateResult"),i=this.options.get("escapeMarkup"),r=n(e,t);null==r?t.style.display="none":"string"==typeof r?t.innerHTML=i(r):h(t).append(r)},i}),e.define("select2/keys",[],function(){return{BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46}}),e.define("select2/selection/base",["jquery","../utils","../keys"],function(n,i,r){function o(e,t){this.$element=e,this.options=t,o.__super__.constructor.call(this)}return i.Extend(o,i.Observable),o.prototype.render=function(){var e=n('<span class="select2-selection" role="combobox"  aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=i.GetData(this.$element[0],"old-tabindex")?this._tabindex=i.GetData(this.$element[0],"old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),e.attr("title",this.$element.attr("title")),e.attr("tabindex",this._tabindex),e.attr("aria-disabled","false"),this.$selection=e},o.prototype.bind=function(e,t){var n=this,i=e.id+"-results";this.container=e,this.$selection.on("focus",function(e){n.trigger("focus",e)}),this.$selection.on("blur",function(e){n._handleBlur(e)}),this.$selection.on("keydown",function(e){n.trigger("keypress",e),e.which===r.SPACE&&e.preventDefault()}),e.on("results:focus",function(e){n.$selection.attr("aria-activedescendant",e.data._resultId)}),e.on("selection:update",function(e){n.update(e.data)}),e.on("open",function(){n.$selection.attr("aria-expanded","true"),n.$selection.attr("aria-owns",i),n._attachCloseHandler(e)}),e.on("close",function(){n.$selection.attr("aria-expanded","false"),n.$selection.removeAttr("aria-activedescendant"),n.$selection.removeAttr("aria-owns"),n.$selection.trigger("focus"),n._detachCloseHandler(e)}),e.on("enable",function(){n.$selection.attr("tabindex",n._tabindex),n.$selection.attr("aria-disabled","false")}),e.on("disable",function(){n.$selection.attr("tabindex","-1"),n.$selection.attr("aria-disabled","true")})},o.prototype._handleBlur=function(e){var t=this;window.setTimeout(function(){document.activeElement==t.$selection[0]||n.contains(t.$selection[0],document.activeElement)||t.trigger("blur",e)},1)},o.prototype._attachCloseHandler=function(e){n(document.body).on("mousedown.select2."+e.id,function(e){var t=n(e.target).closest(".select2");n(".select2.select2-container--open").each(function(){this!=t[0]&&i.GetData(this,"element").select2("close")})})},o.prototype._detachCloseHandler=function(e){n(document.body).off("mousedown.select2."+e.id)},o.prototype.position=function(e,t){t.find(".selection").append(e)},o.prototype.destroy=function(){this._detachCloseHandler(this.container)},o.prototype.update=function(e){throw new Error("The `update` method must be defined in child classes.")},o}),e.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(e,t,n,i){function r(){r.__super__.constructor.apply(this,arguments)}return n.Extend(r,t),r.prototype.render=function(){var e=r.__super__.render.call(this);return e.addClass("select2-selection--single"),e.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),e},r.prototype.bind=function(t,e){var n=this;r.__super__.bind.apply(this,arguments);var i=t.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",i).attr("role","textbox").attr("aria-readonly","true"),this.$selection.attr("aria-labelledby",i),this.$selection.on("mousedown",function(e){1===e.which&&n.trigger("toggle",{originalEvent:e})}),this.$selection.on("focus",function(e){}),this.$selection.on("blur",function(e){}),t.on("focus",function(e){t.isOpen()||n.$selection.trigger("focus")})},r.prototype.clear=function(){var e=this.$selection.find(".select2-selection__rendered");e.empty(),e.removeAttr("title")},r.prototype.display=function(e,t){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(e,t))},r.prototype.selectionContainer=function(){return e("<span></span>")},r.prototype.update=function(e){if(0!==e.length){var t=e[0],n=this.$selection.find(".select2-selection__rendered"),i=this.display(t,n);n.empty().append(i);var r=t.title||t.text;r?n.attr("title",r):n.removeAttr("title")}else this.clear()},r}),e.define("select2/selection/multiple",["jquery","./base","../utils"],function(r,e,l){function n(e,t){n.__super__.constructor.apply(this,arguments)}return l.Extend(n,e),n.prototype.render=function(){var e=n.__super__.render.call(this);return e.addClass("select2-selection--multiple"),e.html('<ul class="select2-selection__rendered"></ul>'),e},n.prototype.bind=function(e,t){var i=this;n.__super__.bind.apply(this,arguments),this.$selection.on("click",function(e){i.trigger("toggle",{originalEvent:e})}),this.$selection.on("click",".select2-selection__choice__remove",function(e){if(!i.options.get("disabled")){var t=r(this).parent(),n=l.GetData(t[0],"data");i.trigger("unselect",{originalEvent:e,data:n})}})},n.prototype.clear=function(){var e=this.$selection.find(".select2-selection__rendered");e.empty(),e.removeAttr("title")},n.prototype.display=function(e,t){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(e,t))},n.prototype.selectionContainer=function(){return r('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">&#10799;</span></li>')},n.prototype.update=function(e){if(this.clear(),0!==e.length){for(var t=[],n=0;n<e.length;n++){var i=e[n],r=this.selectionContainer(),o=this.display(i,r);r.append(o);var s=i.title||i.text;s&&r.attr("title",s),l.StoreData(r[0],"data",i),t.push(r)}var a=this.$selection.find(".select2-selection__rendered");l.appendMany(a,t)}},n}),e.define("select2/selection/placeholder",["../utils"],function(e){function t(e,t,n){this.placeholder=this.normalizePlaceholder(n.get("placeholder")),e.call(this,t,n)}return t.prototype.normalizePlaceholder=function(e,t){return"string"==typeof t&&(t={id:"",text:t}),t},t.prototype.createPlaceholder=function(e,t){var n=this.selectionContainer();return n.html(this.display(t)),n.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),n},t.prototype.update=function(e,t){var n=1==t.length&&t[0].id!=this.placeholder.id;if(1<t.length||n)return e.call(this,t);this.clear();var i=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(i)},t}),e.define("select2/selection/allowClear",["jquery","../keys","../utils"],function(r,i,a){function e(){}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(e){i._handleClear(e)}),t.on("keypress",function(e){i._handleKeyboardClear(e,t)})},e.prototype._handleClear=function(e,t){if(!this.options.get("disabled")){var n=this.$selection.find(".select2-selection__clear");if(0!==n.length){t.stopPropagation();var i=a.GetData(n[0],"data"),r=this.$element.val();this.$element.val(this.placeholder.id);var o={data:i};if(this.trigger("clear",o),o.prevented)this.$element.val(r);else{for(var s=0;s<i.length;s++)if(o={data:i[s]},this.trigger("unselect",o),o.prevented)return void this.$element.val(r);this.$element.trigger("change"),this.trigger("toggle",{})}}}},e.prototype._handleKeyboardClear=function(e,t,n){n.isOpen()||t.which!=i.DELETE&&t.which!=i.BACKSPACE||this._handleClear(t)},e.prototype.update=function(e,t){if(e.call(this,t),!(0<this.$selection.find(".select2-selection__placeholder").length||0===t.length)){var n=this.options.get("translations").get("removeAllItems"),i=r('<span class="select2-selection__clear" title="'+n()+'">&#10799;</span>');a.StoreData(i[0],"data",t),this.$selection.find(".select2-selection__rendered").prepend(i)}},e}),e.define("select2/selection/search",["jquery","../utils","../keys"],function(i,a,l){function e(e,t,n){e.call(this,t,n)}return e.prototype.render=function(e){var t=i('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false" role="searchbox" aria-autocomplete="list" /></li>');this.$searchContainer=t,this.$search=t.find("input");var n=e.call(this);return this._transferTabIndex(),n},e.prototype.bind=function(e,t,n){var i=this,r=t.id+"-results";e.call(this,t,n),t.on("open",function(){i.$search.attr("aria-controls",r),i.$search.trigger("focus")}),t.on("close",function(){i.$search.val(""),i.$search.removeAttr("aria-controls"),i.$search.removeAttr("aria-activedescendant"),i.$search.trigger("focus")}),t.on("enable",function(){i.$search.prop("disabled",!1),i._transferTabIndex()}),t.on("disable",function(){i.$search.prop("disabled",!0)}),t.on("focus",function(e){i.$search.trigger("focus")}),t.on("results:focus",function(e){e.data._resultId?i.$search.attr("aria-activedescendant",e.data._resultId):i.$search.removeAttr("aria-activedescendant")}),this.$selection.on("focusin",".select2-search--inline",function(e){i.trigger("focus",e)}),this.$selection.on("focusout",".select2-search--inline",function(e){i._handleBlur(e)}),this.$selection.on("keydown",".select2-search--inline",function(e){if(e.stopPropagation(),i.trigger("keypress",e),i._keyUpPrevented=e.isDefaultPrevented(),e.which===l.BACKSPACE&&""===i.$search.val()){var t=i.$searchContainer.prev(".select2-selection__choice");if(0<t.length){var n=a.GetData(t[0],"data");i.searchRemoveChoice(n),e.preventDefault()}}}),this.$selection.on("click",".select2-search--inline",function(e){i.$search.val()&&e.stopPropagation()});var o=document.documentMode,s=o&&o<=11;this.$selection.on("input.searchcheck",".select2-search--inline",function(e){s?i.$selection.off("input.search input.searchcheck"):i.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(e){if(s&&"input"===e.type)i.$selection.off("input.search input.searchcheck");else{var t=e.which;t!=l.SHIFT&&t!=l.CTRL&&t!=l.ALT&&t!=l.TAB&&i.handleSearch(e)}})},e.prototype._transferTabIndex=function(e){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},e.prototype.createPlaceholder=function(e,t){this.$search.attr("placeholder",t.text)},e.prototype.update=function(e,t){var n=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),e.call(this,t),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),n&&this.$search.trigger("focus")},e.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var e=this.$search.val();this.trigger("query",{term:e})}this._keyUpPrevented=!1},e.prototype.searchRemoveChoice=function(e,t){this.trigger("unselect",{data:t}),this.$search.val(t.text),this.handleSearch()},e.prototype.resizeSearch=function(){this.$search.css("width","25px");var e="";""!==this.$search.attr("placeholder")?e=this.$selection.find(".select2-selection__rendered").width():e=.75*(this.$search.val().length+1)+"em";this.$search.css("width",e)},e}),e.define("select2/selection/eventRelay",["jquery"],function(s){function e(){}return e.prototype.bind=function(e,t,n){var i=this,r=["open","opening","close","closing","select","selecting","unselect","unselecting","clear","clearing"],o=["opening","closing","selecting","unselecting","clearing"];e.call(this,t,n),t.on("*",function(e,t){if(-1!==s.inArray(e,r)){t=t||{};var n=s.Event("select2:"+e,{params:t});i.$element.trigger(n),-1!==s.inArray(e,o)&&(t.prevented=n.isDefaultPrevented())}})},e}),e.define("select2/translation",["jquery","require"],function(t,n){function i(e){this.dict=e||{}}return i.prototype.all=function(){return this.dict},i.prototype.get=function(e){return this.dict[e]},i.prototype.extend=function(e){this.dict=t.extend({},e.all(),this.dict)},i._cache={},i.loadPath=function(e){if(!(e in i._cache)){var t=n(e);i._cache[e]=t}return new i(i._cache[e])},i}),e.define("select2/diacritics",[],function(){return{"Ⓐ":"A","Ａ":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","Ｂ":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","Ｃ":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","Ｄ":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","Ǳ":"DZ","Ǆ":"DZ","ǲ":"Dz","ǅ":"Dz","Ⓔ":"E","Ｅ":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","Ｆ":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","Ｇ":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","Ｈ":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","Ｉ":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","Ｊ":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","Ｋ":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","Ｌ":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","Ǉ":"LJ","ǈ":"Lj","Ⓜ":"M","Ｍ":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","Ｎ":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","Ǌ":"NJ","ǋ":"Nj","Ⓞ":"O","Ｏ":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Œ":"OE","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","Ｐ":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Ｑ":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","Ｒ":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","Ｓ":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","Ｔ":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","Ｕ":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","Ｖ":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","Ｗ":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","Ｘ":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Ｙ":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Ｚ":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","ａ":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","ｂ":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","ｃ":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","ｄ":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","ǳ":"dz","ǆ":"dz","ⓔ":"e","ｅ":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","ｆ":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","ｇ":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","ｈ":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","ｉ":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","ｊ":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","ｋ":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","ｌ":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","ǉ":"lj","ⓜ":"m","ｍ":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","ｎ":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ŉ":"n","ꞑ":"n","ꞥ":"n","ǌ":"nj","ⓞ":"o","ｏ":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","œ":"oe","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","ｐ":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","ｑ":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","ｒ":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","ｓ":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","ｔ":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","ｕ":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","ｖ":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","ｗ":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","ｘ":"x","ẋ":"x","ẍ":"x","ⓨ":"y","ｙ":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","ｚ":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ώ":"ω","ς":"σ","’":"'"}}),e.define("select2/data/base",["../utils"],function(i){function n(e,t){n.__super__.constructor.call(this)}return i.Extend(n,i.Observable),n.prototype.current=function(e){throw new Error("The `current` method must be defined in child classes.")},n.prototype.query=function(e,t){throw new Error("The `query` method must be defined in child classes.")},n.prototype.bind=function(e,t){},n.prototype.destroy=function(){},n.prototype.generateResultId=function(e,t){var n=e.id+"-result-";return n+=i.generateChars(4),null!=t.id?n+="-"+t.id.toString():n+="-"+i.generateChars(4),n},n}),e.define("select2/data/select",["./base","../utils","jquery"],function(e,a,l){function n(e,t){this.$element=e,this.options=t,n.__super__.constructor.call(this)}return a.Extend(n,e),n.prototype.current=function(e){var n=[],i=this;this.$element.find(":selected").each(function(){var e=l(this),t=i.item(e);n.push(t)}),e(n)},n.prototype.select=function(r){var o=this;if(r.selected=!0,l(r.element).is("option"))return r.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(e){var t=[];(r=[r]).push.apply(r,e);for(var n=0;n<r.length;n++){var i=r[n].id;-1===l.inArray(i,t)&&t.push(i)}o.$element.val(t),o.$element.trigger("change")});else{var e=r.id;this.$element.val(e),this.$element.trigger("change")}},n.prototype.unselect=function(r){var o=this;if(this.$element.prop("multiple")){if(r.selected=!1,l(r.element).is("option"))return r.element.selected=!1,void this.$element.trigger("change");this.current(function(e){for(var t=[],n=0;n<e.length;n++){var i=e[n].id;i!==r.id&&-1===l.inArray(i,t)&&t.push(i)}o.$element.val(t),o.$element.trigger("change")})}},n.prototype.bind=function(e,t){var n=this;(this.container=e).on("select",function(e){n.select(e.data)}),e.on("unselect",function(e){n.unselect(e.data)})},n.prototype.destroy=function(){this.$element.find("*").each(function(){a.RemoveData(this)})},n.prototype.query=function(i,e){var r=[],o=this;this.$element.children().each(function(){var e=l(this);if(e.is("option")||e.is("optgroup")){var t=o.item(e),n=o.matches(i,t);null!==n&&r.push(n)}}),e({results:r})},n.prototype.addOptions=function(e){a.appendMany(this.$element,e)},n.prototype.option=function(e){var t;e.children?(t=document.createElement("optgroup")).label=e.text:void 0!==(t=document.createElement("option")).textContent?t.textContent=e.text:t.innerText=e.text,void 0!==e.id&&(t.value=e.id),e.disabled&&(t.disabled=!0),e.selected&&(t.selected=!0),e.title&&(t.title=e.title);var n=l(t),i=this._normalizeItem(e);return i.element=t,a.StoreData(t,"data",i),n},n.prototype.item=function(e){var t={};if(null!=(t=a.GetData(e[0],"data")))return t;if(e.is("option"))t={id:e.val(),text:e.text(),disabled:e.prop("disabled"),selected:e.prop("selected"),title:e.prop("title")};else if(e.is("optgroup")){t={text:e.prop("label"),children:[],title:e.prop("title")};for(var n=e.children("option"),i=[],r=0;r<n.length;r++){var o=l(n[r]),s=this.item(o);i.push(s)}t.children=i}return(t=this._normalizeItem(t)).element=e[0],a.StoreData(e[0],"data",t),t},n.prototype._normalizeItem=function(e){e!==Object(e)&&(e={id:e,text:e});return null!=(e=l.extend({},{text:""},e)).id&&(e.id=e.id.toString()),null!=e.text&&(e.text=e.text.toString()),null==e._resultId&&e.id&&null!=this.container&&(e._resultId=this.generateResultId(this.container,e)),l.extend({},{selected:!1,disabled:!1},e)},n.prototype.matches=function(e,t){return this.options.get("matcher")(e,t)},n}),e.define("select2/data/array",["./select","../utils","jquery"],function(e,f,g){function i(e,t){this._dataToConvert=t.get("data")||[],i.__super__.constructor.call(this,e,t)}return f.Extend(i,e),i.prototype.bind=function(e,t){i.__super__.bind.call(this,e,t),this.addOptions(this.convertToOptions(this._dataToConvert))},i.prototype.select=function(n){var e=this.$element.find("option").filter(function(e,t){return t.value==n.id.toString()});0===e.length&&(e=this.option(n),this.addOptions(e)),i.__super__.select.call(this,n)},i.prototype.convertToOptions=function(e){var t=this,n=this.$element.find("option"),i=n.map(function(){return t.item(g(this)).id}).get(),r=[];function o(e){return function(){return g(this).val()==e.id}}for(var s=0;s<e.length;s++){var a=this._normalizeItem(e[s]);if(0<=g.inArray(a.id,i)){var l=n.filter(o(a)),c=this.item(l),u=g.extend(!0,{},a,c),d=this.option(u);l.replaceWith(d)}else{var p=this.option(a);if(a.children){var h=this.convertToOptions(a.children);f.appendMany(p,h)}r.push(p)}}return r},i}),e.define("select2/data/ajax",["./array","../utils","jquery"],function(e,t,o){function n(e,t){this.ajaxOptions=this._applyDefaults(t.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),n.__super__.constructor.call(this,e,t)}return t.Extend(n,e),n.prototype._applyDefaults=function(e){var t={data:function(e){return o.extend({},e,{q:e.term})},transport:function(e,t,n){var i=o.ajax(e);return i.then(t),i.fail(n),i}};return o.extend({},t,e,!0)},n.prototype.processResults=function(e){return e},n.prototype.query=function(n,i){var r=this;null!=this._request&&(o.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var t=o.extend({type:"GET"},this.ajaxOptions);function e(){var e=t.transport(t,function(e){var t=r.processResults(e,n);r.options.get("debug")&&window.console&&console.error&&(t&&t.results&&o.isArray(t.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),i(t)},function(){"status"in e&&(0===e.status||"0"===e.status)||r.trigger("results:message",{message:"errorLoading"})});r._request=e}"function"==typeof t.url&&(t.url=t.url.call(this.$element,n)),"function"==typeof t.data&&(t.data=t.data.call(this.$element,n)),this.ajaxOptions.delay&&null!=n.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(e,this.ajaxOptions.delay)):e()},n}),e.define("select2/data/tags",["jquery"],function(u){function e(e,t,n){var i=n.get("tags"),r=n.get("createTag");void 0!==r&&(this.createTag=r);var o=n.get("insertTag");if(void 0!==o&&(this.insertTag=o),e.call(this,t,n),u.isArray(i))for(var s=0;s<i.length;s++){var a=i[s],l=this._normalizeItem(a),c=this.option(l);this.$element.append(c)}}return e.prototype.query=function(e,c,u){var d=this;this._removeOldTags(),null!=c.term&&null==c.page?e.call(this,c,function e(t,n){for(var i=t.results,r=0;r<i.length;r++){var o=i[r],s=null!=o.children&&!e({results:o.children},!0);if((o.text||"").toUpperCase()===(c.term||"").toUpperCase()||s)return!n&&(t.data=i,void u(t))}if(n)return!0;var a=d.createTag(c);if(null!=a){var l=d.option(a);l.attr("data-select2-tag",!0),d.addOptions([l]),d.insertTag(i,a)}t.results=i,u(t)}):e.call(this,c,u)},e.prototype.createTag=function(e,t){var n=u.trim(t.term);return""===n?null:{id:n,text:n}},e.prototype.insertTag=function(e,t,n){t.unshift(n)},e.prototype._removeOldTags=function(e){this.$element.find("option[data-select2-tag]").each(function(){this.selected||u(this).remove()})},e}),e.define("select2/data/tokenizer",["jquery"],function(d){function e(e,t,n){var i=n.get("tokenizer");void 0!==i&&(this.tokenizer=i),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){e.call(this,t,n),this.$search=t.dropdown.$search||t.selection.$search||n.find(".select2-search__field")},e.prototype.query=function(e,t,n){var i=this;t.term=t.term||"";var r=this.tokenizer(t,this.options,function(e){var t=i._normalizeItem(e);if(!i.$element.find("option").filter(function(){return d(this).val()===t.id}).length){var n=i.option(t);n.attr("data-select2-tag",!0),i._removeOldTags(),i.addOptions([n])}!function(e){i.trigger("select",{data:e})}(t)});r.term!==t.term&&(this.$search.length&&(this.$search.val(r.term),this.$search.trigger("focus")),t.term=r.term),e.call(this,t,n)},e.prototype.tokenizer=function(e,t,n,i){for(var r=n.get("tokenSeparators")||[],o=t.term,s=0,a=this.createTag||function(e){return{id:e.term,text:e.term}};s<o.length;){var l=o[s];if(-1!==d.inArray(l,r)){var c=o.substr(0,s),u=a(d.extend({},t,{term:c}));null!=u?(i(u),o=o.substr(s+1)||"",s=0):s++}else s++}return{term:o}},e}),e.define("select2/data/minimumInputLength",[],function(){function e(e,t,n){this.minimumInputLength=n.get("minimumInputLength"),e.call(this,t,n)}return e.prototype.query=function(e,t,n){t.term=t.term||"",t.term.length<this.minimumInputLength?this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:t.term,params:t}}):e.call(this,t,n)},e}),e.define("select2/data/maximumInputLength",[],function(){function e(e,t,n){this.maximumInputLength=n.get("maximumInputLength"),e.call(this,t,n)}return e.prototype.query=function(e,t,n){t.term=t.term||"",0<this.maximumInputLength&&t.term.length>this.maximumInputLength?this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:t.term,params:t}}):e.call(this,t,n)},e}),e.define("select2/data/maximumSelectionLength",[],function(){function e(e,t,n){this.maximumSelectionLength=n.get("maximumSelectionLength"),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("select",function(){i._checkIfMaximumSelected()})},e.prototype.query=function(e,t,n){var i=this;this._checkIfMaximumSelected(function(){e.call(i,t,n)})},e.prototype._checkIfMaximumSelected=function(e,n){var i=this;this.current(function(e){var t=null!=e?e.length:0;0<i.maximumSelectionLength&&t>=i.maximumSelectionLength?i.trigger("results:message",{message:"maximumSelected",args:{maximum:i.maximumSelectionLength}}):n&&n()})},e}),e.define("select2/dropdown",["jquery","./utils"],function(t,e){function n(e,t){this.$element=e,this.options=t,n.__super__.constructor.call(this)}return e.Extend(n,e.Observable),n.prototype.render=function(){var e=t('<span class="select2-dropdown"><span class="select2-results"></span></span>');return e.attr("dir",this.options.get("dir")),this.$dropdown=e},n.prototype.bind=function(){},n.prototype.position=function(e,t){},n.prototype.destroy=function(){this.$dropdown.remove()},n}),e.define("select2/dropdown/search",["jquery","../utils"],function(o,e){function t(){}return t.prototype.render=function(e){var t=e.call(this),n=o('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false" role="searchbox" aria-autocomplete="list" /></span>');return this.$searchContainer=n,this.$search=n.find("input"),t.prepend(n),t},t.prototype.bind=function(e,t,n){var i=this,r=t.id+"-results";e.call(this,t,n),this.$search.on("keydown",function(e){i.trigger("keypress",e),i._keyUpPrevented=e.isDefaultPrevented()}),this.$search.on("input",function(e){o(this).off("keyup")}),this.$search.on("keyup input",function(e){i.handleSearch(e)}),t.on("open",function(){i.$search.attr("tabindex",0),i.$search.attr("aria-controls",r),i.$search.trigger("focus"),window.setTimeout(function(){i.$search.trigger("focus")},0)}),t.on("close",function(){i.$search.attr("tabindex",-1),i.$search.removeAttr("aria-controls"),i.$search.removeAttr("aria-activedescendant"),i.$search.val(""),i.$search.trigger("blur")}),t.on("focus",function(){t.isOpen()||i.$search.trigger("focus")}),t.on("results:all",function(e){null!=e.query.term&&""!==e.query.term||(i.showSearch(e)?i.$searchContainer.removeClass("select2-search--hide"):i.$searchContainer.addClass("select2-search--hide"))}),t.on("results:focus",function(e){e.data._resultId?i.$search.attr("aria-activedescendant",e.data._resultId):i.$search.removeAttr("aria-activedescendant")})},t.prototype.handleSearch=function(e){if(!this._keyUpPrevented){var t=this.$search.val();this.trigger("query",{term:t})}this._keyUpPrevented=!1},t.prototype.showSearch=function(e,t){return!0},t}),e.define("select2/dropdown/hidePlaceholder",[],function(){function e(e,t,n,i){this.placeholder=this.normalizePlaceholder(n.get("placeholder")),e.call(this,t,n,i)}return e.prototype.append=function(e,t){t.results=this.removePlaceholder(t.results),e.call(this,t)},e.prototype.normalizePlaceholder=function(e,t){return"string"==typeof t&&(t={id:"",text:t}),t},e.prototype.removePlaceholder=function(e,t){for(var n=t.slice(0),i=t.length-1;0<=i;i--){var r=t[i];this.placeholder.id===r.id&&n.splice(i,1)}return n},e}),e.define("select2/dropdown/infiniteScroll",["jquery"],function(n){function e(e,t,n,i){this.lastParams={},e.call(this,t,n,i),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return e.prototype.append=function(e,t){this.$loadingMore.remove(),this.loading=!1,e.call(this,t),this.showLoadingMore(t)&&(this.$results.append(this.$loadingMore),this.loadMoreIfNeeded())},e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("query",function(e){i.lastParams=e,i.loading=!0}),t.on("query:append",function(e){i.lastParams=e,i.loading=!0}),this.$results.on("scroll",this.loadMoreIfNeeded.bind(this))},e.prototype.loadMoreIfNeeded=function(){var e=n.contains(document.documentElement,this.$loadingMore[0]);if(!this.loading&&e){var t=this.$results.offset().top+this.$results.outerHeight(!1);this.$loadingMore.offset().top+this.$loadingMore.outerHeight(!1)<=t+50&&this.loadMore()}},e.prototype.loadMore=function(){this.loading=!0;var e=n.extend({},{page:1},this.lastParams);e.page++,this.trigger("query:append",e)},e.prototype.showLoadingMore=function(e,t){return t.pagination&&t.pagination.more},e.prototype.createLoadingMore=function(){var e=n('<li class="select2-results__option select2-results__option--load-more"role="option" aria-disabled="true"></li>'),t=this.options.get("translations").get("loadingMore");return e.html(t(this.lastParams)),e},e}),e.define("select2/dropdown/attachBody",["jquery","../utils"],function(f,a){function e(e,t,n){this.$dropdownParent=f(n.get("dropdownParent")||document.body),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("open",function(){i._showDropdown(),i._attachPositioningHandler(t),i._bindContainerResultHandlers(t)}),t.on("close",function(){i._hideDropdown(),i._detachPositioningHandler(t)}),this.$dropdownContainer.on("mousedown",function(e){e.stopPropagation()})},e.prototype.destroy=function(e){e.call(this),this.$dropdownContainer.remove()},e.prototype.position=function(e,t,n){t.attr("class",n.attr("class")),t.removeClass("select2"),t.addClass("select2-container--open"),t.css({position:"absolute",top:-999999}),this.$container=n},e.prototype.render=function(e){var t=f("<span></span>"),n=e.call(this);return t.append(n),this.$dropdownContainer=t},e.prototype._hideDropdown=function(e){this.$dropdownContainer.detach()},e.prototype._bindContainerResultHandlers=function(e,t){if(!this._containerResultsHandlersBound){var n=this;t.on("results:all",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("results:append",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("results:message",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("select",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("unselect",function(){n._positionDropdown(),n._resizeDropdown()}),this._containerResultsHandlersBound=!0}},e.prototype._attachPositioningHandler=function(e,t){var n=this,i="scroll.select2."+t.id,r="resize.select2."+t.id,o="orientationchange.select2."+t.id,s=this.$container.parents().filter(a.hasScroll);s.each(function(){a.StoreData(this,"select2-scroll-position",{x:f(this).scrollLeft(),y:f(this).scrollTop()})}),s.on(i,function(e){var t=a.GetData(this,"select2-scroll-position");f(this).scrollTop(t.y)}),f(window).on(i+" "+r+" "+o,function(e){n._positionDropdown(),n._resizeDropdown()})},e.prototype._detachPositioningHandler=function(e,t){var n="scroll.select2."+t.id,i="resize.select2."+t.id,r="orientationchange.select2."+t.id;this.$container.parents().filter(a.hasScroll).off(n),f(window).off(n+" "+i+" "+r)},e.prototype._positionDropdown=function(){var e=f(window),t=this.$dropdown.hasClass("select2-dropdown--above"),n=this.$dropdown.hasClass("select2-dropdown--below"),i=null,r=this.$container.offset();r.bottom=r.top+this.$container.outerHeight(!1);var o={height:this.$container.outerHeight(!1)};o.top=r.top,o.bottom=r.top+o.height;var s=this.$dropdown.outerHeight(!1),a=e.scrollTop(),l=e.scrollTop()+e.height(),c=a<r.top-s,u=l>r.bottom+s,d={left:r.left,top:o.bottom},p=this.$dropdownParent;"static"===p.css("position")&&(p=p.offsetParent());var h={top:0,left:0};(f.contains(document.body,p[0])||p[0].isConnected)&&(h=p.offset()),d.top-=h.top,d.left-=h.left,t||n||(i="below"),u||!c||t?!c&&u&&t&&(i="below"):i="above",("above"==i||t&&"below"!==i)&&(d.top=o.top-h.top-s),null!=i&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+i),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+i)),this.$dropdownContainer.css(d)},e.prototype._resizeDropdown=function(){var e={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(e.minWidth=e.width,e.position="relative",e.width="auto"),this.$dropdown.css(e)},e.prototype._showDropdown=function(e){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},e}),e.define("select2/dropdown/minimumResultsForSearch",[],function(){function e(e,t,n,i){this.minimumResultsForSearch=n.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),e.call(this,t,n,i)}return e.prototype.showSearch=function(e,t){return!(function e(t){for(var n=0,i=0;i<t.length;i++){var r=t[i];r.children?n+=e(r.children):n++}return n}(t.data.results)<this.minimumResultsForSearch)&&e.call(this,t)},e}),e.define("select2/dropdown/selectOnClose",["../utils"],function(o){function e(){}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("close",function(e){i._handleSelectOnClose(e)})},e.prototype._handleSelectOnClose=function(e,t){if(t&&null!=t.originalSelect2Event){var n=t.originalSelect2Event;if("select"===n._type||"unselect"===n._type)return}var i=this.getHighlightedResults();if(!(i.length<1)){var r=o.GetData(i[0],"data");null!=r.element&&r.element.selected||null==r.element&&r.selected||this.trigger("select",{data:r})}},e}),e.define("select2/dropdown/closeOnSelect",[],function(){function e(){}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("select",function(e){i._selectTriggered(e)}),t.on("unselect",function(e){i._selectTriggered(e)})},e.prototype._selectTriggered=function(e,t){var n=t.originalEvent;n&&(n.ctrlKey||n.metaKey)||this.trigger("close",{originalEvent:n,originalSelect2Event:t})},e}),e.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Please delete "+t+" character";return 1!=t&&(n+="s"),n},inputTooShort:function(e){return"Please enter "+(e.minimum-e.input.length)+" or more characters"},loadingMore:function(){return"Loading more results…"},maximumSelected:function(e){var t="You can only select "+e.maximum+" item";return 1!=e.maximum&&(t+="s"),t},noResults:function(){return"No results found"},searching:function(){return"Searching…"},removeAllItems:function(){return"Remove all items"}}}),e.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(c,u,d,p,h,f,g,m,v,y,s,t,_,w,$,b,A,x,S,D,C,E,O,T,q,j,L,I,e){function n(){this.reset()}return n.prototype.apply=function(e){if(null==(e=c.extend(!0,{},this.defaults,e)).dataAdapter){if(null!=e.ajax?e.dataAdapter=$:null!=e.data?e.dataAdapter=w:e.dataAdapter=_,0<e.minimumInputLength&&(e.dataAdapter=y.Decorate(e.dataAdapter,x)),0<e.maximumInputLength&&(e.dataAdapter=y.Decorate(e.dataAdapter,S)),0<e.maximumSelectionLength&&(e.dataAdapter=y.Decorate(e.dataAdapter,D)),e.tags&&(e.dataAdapter=y.Decorate(e.dataAdapter,b)),null==e.tokenSeparators&&null==e.tokenizer||(e.dataAdapter=y.Decorate(e.dataAdapter,A)),null!=e.query){var t=u(e.amdBase+"compat/query");e.dataAdapter=y.Decorate(e.dataAdapter,t)}if(null!=e.initSelection){var n=u(e.amdBase+"compat/initSelection");e.dataAdapter=y.Decorate(e.dataAdapter,n)}}if(null==e.resultsAdapter&&(e.resultsAdapter=d,null!=e.ajax&&(e.resultsAdapter=y.Decorate(e.resultsAdapter,T)),null!=e.placeholder&&(e.resultsAdapter=y.Decorate(e.resultsAdapter,O)),e.selectOnClose&&(e.resultsAdapter=y.Decorate(e.resultsAdapter,L))),null==e.dropdownAdapter){if(e.multiple)e.dropdownAdapter=C;else{var i=y.Decorate(C,E);e.dropdownAdapter=i}if(0!==e.minimumResultsForSearch&&(e.dropdownAdapter=y.Decorate(e.dropdownAdapter,j)),e.closeOnSelect&&(e.dropdownAdapter=y.Decorate(e.dropdownAdapter,I)),null!=e.dropdownCssClass||null!=e.dropdownCss||null!=e.adaptDropdownCssClass){var r=u(e.amdBase+"compat/dropdownCss");e.dropdownAdapter=y.Decorate(e.dropdownAdapter,r)}e.dropdownAdapter=y.Decorate(e.dropdownAdapter,q)}if(null==e.selectionAdapter){if(e.multiple?e.selectionAdapter=h:e.selectionAdapter=p,null!=e.placeholder&&(e.selectionAdapter=y.Decorate(e.selectionAdapter,f)),e.allowClear&&(e.selectionAdapter=y.Decorate(e.selectionAdapter,g)),e.multiple&&(e.selectionAdapter=y.Decorate(e.selectionAdapter,m)),null!=e.containerCssClass||null!=e.containerCss||null!=e.adaptContainerCssClass){var o=u(e.amdBase+"compat/containerCss");e.selectionAdapter=y.Decorate(e.selectionAdapter,o)}e.selectionAdapter=y.Decorate(e.selectionAdapter,v)}e.language=this._resolveLanguage(e.language),e.language.push("en");for(var s=[],a=0;a<e.language.length;a++){var l=e.language[a];-1===s.indexOf(l)&&s.push(l)}return e.language=s,e.translations=this._processTranslations(e.language,e.debug),e},n.prototype.reset=function(){function a(e){return e.replace(/[^\u0000-\u007E]/g,function(e){return t[e]||e})}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:y.escapeMarkup,language:{},matcher:function e(t,n){if(""===c.trim(t.term))return n;if(n.children&&0<n.children.length){for(var i=c.extend(!0,{},n),r=n.children.length-1;0<=r;r--)null==e(t,n.children[r])&&i.children.splice(r,1);return 0<i.children.length?i:e(t,i)}var o=a(n.text).toUpperCase(),s=a(t.term).toUpperCase();return-1<o.indexOf(s)?n:null},minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,scrollAfterSelect:!1,sorter:function(e){return e},templateResult:function(e){return e.text},templateSelection:function(e){return e.text},theme:"default",width:"resolve"}},n.prototype.applyFromElement=function(e,t){var n=e.language,i=this.defaults.language,r=t.prop("lang"),o=t.closest("[lang]").prop("lang"),s=Array.prototype.concat.call(this._resolveLanguage(r),this._resolveLanguage(n),this._resolveLanguage(i),this._resolveLanguage(o));return e.language=s,e},n.prototype._resolveLanguage=function(e){if(!e)return[];if(c.isEmptyObject(e))return[];if(c.isPlainObject(e))return[e];var t;t=c.isArray(e)?e:[e];for(var n=[],i=0;i<t.length;i++)if(n.push(t[i]),"string"==typeof t[i]&&0<t[i].indexOf("-")){var r=t[i].split("-")[0];n.push(r)}return n},n.prototype._processTranslations=function(e,t){for(var n=new s,i=0;i<e.length;i++){var r=new s,o=e[i];if("string"==typeof o)try{r=s.loadPath(o)}catch(e){try{o=this.defaults.amdLanguageBase+o,r=s.loadPath(o)}catch(e){t&&window.console&&console.warn&&console.warn('Select2: The language file for "'+o+'" could not be automatically loaded. A fallback will be used instead.')}}else r=c.isPlainObject(o)?new s(o):o;n.extend(r)}return n},n.prototype.set=function(e,t){var n={};n[c.camelCase(e)]=t;var i=y._convertData(n);c.extend(!0,this.defaults,i)},new n}),e.define("select2/options",["require","jquery","./defaults","./utils"],function(i,d,r,p){function e(e,t){if(this.options=e,null!=t&&this.fromElement(t),null!=t&&(this.options=r.applyFromElement(this.options,t)),this.options=r.apply(this.options),t&&t.is("input")){var n=i(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=p.Decorate(this.options.dataAdapter,n)}}return e.prototype.fromElement=function(e){var t=["select2"];null==this.options.multiple&&(this.options.multiple=e.prop("multiple")),null==this.options.disabled&&(this.options.disabled=e.prop("disabled")),null==this.options.dir&&(e.prop("dir")?this.options.dir=e.prop("dir"):e.closest("[dir]").prop("dir")?this.options.dir=e.closest("[dir]").prop("dir"):this.options.dir="ltr"),e.prop("disabled",this.options.disabled),e.prop("multiple",this.options.multiple),p.GetData(e[0],"select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),p.StoreData(e[0],"data",p.GetData(e[0],"select2Tags")),p.StoreData(e[0],"tags",!0)),p.GetData(e[0],"ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),e.attr("ajax--url",p.GetData(e[0],"ajaxUrl")),p.StoreData(e[0],"ajax-Url",p.GetData(e[0],"ajaxUrl")));var n={};function i(e,t){return t.toUpperCase()}for(var r=0;r<e[0].attributes.length;r++){var o=e[0].attributes[r].name,s="data-";if(o.substr(0,s.length)==s){var a=o.substring(s.length),l=p.GetData(e[0],a);n[a.replace(/-([a-z])/g,i)]=l}}d.fn.jquery&&"1."==d.fn.jquery.substr(0,2)&&e[0].dataset&&(n=d.extend(!0,{},e[0].dataset,n));var c=d.extend(!0,{},p.GetData(e[0]),n);for(var u in c=p._convertData(c))-1<d.inArray(u,t)||(d.isPlainObject(this.options[u])?d.extend(this.options[u],c[u]):this.options[u]=c[u]);return this},e.prototype.get=function(e){return this.options[e]},e.prototype.set=function(e,t){this.options[e]=t},e}),e.define("select2/core",["jquery","./options","./utils","./keys"],function(r,c,u,i){var d=function(e,t){null!=u.GetData(e[0],"select2")&&u.GetData(e[0],"select2").destroy(),this.$element=e,this.id=this._generateId(e),t=t||{},this.options=new c(t,e),d.__super__.constructor.call(this);var n=e.attr("tabindex")||0;u.StoreData(e[0],"old-tabindex",n),e.attr("tabindex","-1");var i=this.options.get("dataAdapter");this.dataAdapter=new i(e,this.options);var r=this.render();this._placeContainer(r);var o=this.options.get("selectionAdapter");this.selection=new o(e,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,r);var s=this.options.get("dropdownAdapter");this.dropdown=new s(e,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,r);var a=this.options.get("resultsAdapter");this.results=new a(e,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var l=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(e){l.trigger("selection:update",{data:e})}),e.addClass("select2-hidden-accessible"),e.attr("aria-hidden","true"),this._syncAttributes(),u.StoreData(e[0],"select2",this),e.data("select2",this)};return u.Extend(d,u.Observable),d.prototype._generateId=function(e){return"select2-"+(null!=e.attr("id")?e.attr("id"):null!=e.attr("name")?e.attr("name")+"-"+u.generateChars(2):u.generateChars(4)).replace(/(:|\.|\[|\]|,)/g,"")},d.prototype._placeContainer=function(e){e.insertAfter(this.$element);var t=this._resolveWidth(this.$element,this.options.get("width"));null!=t&&e.css("width",t)},d.prototype._resolveWidth=function(e,t){var n=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==t){var i=this._resolveWidth(e,"style");return null!=i?i:this._resolveWidth(e,"element")}if("element"==t){var r=e.outerWidth(!1);return r<=0?"auto":r+"px"}if("style"!=t)return"computedstyle"!=t?t:window.getComputedStyle(e[0]).width;var o=e.attr("style");if("string"!=typeof o)return null;for(var s=o.split(";"),a=0,l=s.length;a<l;a+=1){var c=s[a].replace(/\s/g,"").match(n);if(null!==c&&1<=c.length)return c[1]}return null},d.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},d.prototype._registerDomEvents=function(){var t=this;this.$element.on("change.select2",function(){t.dataAdapter.current(function(e){t.trigger("selection:update",{data:e})})}),this.$element.on("focus.select2",function(e){t.trigger("focus",e)}),this._syncA=u.bind(this._syncAttributes,this),this._syncS=u.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var e=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=e?(this._observer=new e(function(e){r.each(e,t._syncA),r.each(e,t._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",t._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",t._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",t._syncS,!1))},d.prototype._registerDataEvents=function(){var n=this;this.dataAdapter.on("*",function(e,t){n.trigger(e,t)})},d.prototype._registerSelectionEvents=function(){var n=this,i=["toggle","focus"];this.selection.on("toggle",function(){n.toggleDropdown()}),this.selection.on("focus",function(e){n.focus(e)}),this.selection.on("*",function(e,t){-1===r.inArray(e,i)&&n.trigger(e,t)})},d.prototype._registerDropdownEvents=function(){var n=this;this.dropdown.on("*",function(e,t){n.trigger(e,t)})},d.prototype._registerResultsEvents=function(){var n=this;this.results.on("*",function(e,t){n.trigger(e,t)})},d.prototype._registerEvents=function(){var n=this;this.on("open",function(){n.$container.addClass("select2-container--open")}),this.on("close",function(){n.$container.removeClass("select2-container--open")}),this.on("enable",function(){n.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){n.$container.addClass("select2-container--disabled")}),this.on("blur",function(){n.$container.removeClass("select2-container--focus")}),this.on("query",function(t){n.isOpen()||n.trigger("open",{}),this.dataAdapter.query(t,function(e){n.trigger("results:all",{data:e,query:t})})}),this.on("query:append",function(t){this.dataAdapter.query(t,function(e){n.trigger("results:append",{data:e,query:t})})}),this.on("keypress",function(e){var t=e.which;n.isOpen()?t===i.ESC||t===i.TAB||t===i.UP&&e.altKey?(n.close(),e.preventDefault()):t===i.ENTER?(n.trigger("results:select",{}),e.preventDefault()):t===i.SPACE&&e.ctrlKey?(n.trigger("results:toggle",{}),e.preventDefault()):t===i.UP?(n.trigger("results:previous",{}),e.preventDefault()):t===i.DOWN&&(n.trigger("results:next",{}),e.preventDefault()):(t===i.ENTER||t===i.SPACE||t===i.DOWN&&e.altKey)&&(n.open(),e.preventDefault())})},d.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},d.prototype._syncSubtree=function(e,t){var n=!1,i=this;if(!e||!e.target||"OPTION"===e.target.nodeName||"OPTGROUP"===e.target.nodeName){if(t)if(t.addedNodes&&0<t.addedNodes.length)for(var r=0;r<t.addedNodes.length;r++){t.addedNodes[r].selected&&(n=!0)}else t.removedNodes&&0<t.removedNodes.length&&(n=!0);else n=!0;n&&this.dataAdapter.current(function(e){i.trigger("selection:update",{data:e})})}},d.prototype.trigger=function(e,t){var n=d.__super__.trigger,i={open:"opening",close:"closing",select:"selecting",unselect:"unselecting",clear:"clearing"};if(void 0===t&&(t={}),e in i){var r=i[e],o={prevented:!1,name:e,args:t};if(n.call(this,r,o),o.prevented)return void(t.prevented=!0)}n.call(this,e,t)},d.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},d.prototype.open=function(){this.isOpen()||this.trigger("query",{})},d.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},d.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},d.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},d.prototype.focus=function(e){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},d.prototype.enable=function(e){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),null!=e&&0!==e.length||(e=[!0]);var t=!e[0];this.$element.prop("disabled",t)},d.prototype.data=function(){this.options.get("debug")&&0<arguments.length&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var t=[];return this.dataAdapter.current(function(e){t=e}),t},d.prototype.val=function(e){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==e||0===e.length)return this.$element.val();var t=e[0];r.isArray(t)&&(t=r.map(t,function(e){return e.toString()})),this.$element.val(t).trigger("change")},d.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",u.GetData(this.$element[0],"old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),u.RemoveData(this.$element[0]),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},d.prototype.render=function(){var e=r('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return e.attr("dir",this.options.get("dir")),this.$container=e,this.$container.addClass("select2-container--"+this.options.get("theme")),u.StoreData(e[0],"element",this.$element),e},d}),e.define("select2/compat/utils",["jquery"],function(s){return{syncCssClasses:function(e,t,n){var i,r,o=[];(i=s.trim(e.attr("class")))&&s((i=""+i).split(/\s+/)).each(function(){0===this.indexOf("select2-")&&o.push(this)}),(i=s.trim(t.attr("class")))&&s((i=""+i).split(/\s+/)).each(function(){0!==this.indexOf("select2-")&&null!=(r=n(this))&&o.push(r)}),e.attr("class",o.join(" "))}}}),e.define("select2/compat/containerCss",["jquery","./utils"],function(s,a){function l(e){return null}function e(){}return e.prototype.render=function(e){var t=e.call(this),n=this.options.get("containerCssClass")||"";s.isFunction(n)&&(n=n(this.$element));var i=this.options.get("adaptContainerCssClass");if(i=i||l,-1!==n.indexOf(":all:")){n=n.replace(":all:","");var r=i;i=function(e){var t=r(e);return null!=t?t+" "+e:e}}var o=this.options.get("containerCss")||{};return s.isFunction(o)&&(o=o(this.$element)),a.syncCssClasses(t,this.$element,i),t.css(o),t.addClass(n),t},e}),e.define("select2/compat/dropdownCss",["jquery","./utils"],function(s,a){function l(e){return null}function e(){}return e.prototype.render=function(e){var t=e.call(this),n=this.options.get("dropdownCssClass")||"";s.isFunction(n)&&(n=n(this.$element));var i=this.options.get("adaptDropdownCssClass");if(i=i||l,-1!==n.indexOf(":all:")){n=n.replace(":all:","");var r=i;i=function(e){var t=r(e);return null!=t?t+" "+e:e}}var o=this.options.get("dropdownCss")||{};return s.isFunction(o)&&(o=o(this.$element)),a.syncCssClasses(t,this.$element,i),t.css(o),t.addClass(n),t},e}),e.define("select2/compat/initSelection",["jquery"],function(i){function e(e,t,n){n.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `initSelection` option has been deprecated in favor of a custom data adapter that overrides the `current` method. This method is now called multiple times instead of a single time when the instance is initialized. Support will be removed for the `initSelection` option in future versions of Select2"),this.initSelection=n.get("initSelection"),this._isInitialized=!1,e.call(this,t,n)}return e.prototype.current=function(e,t){var n=this;this._isInitialized?e.call(this,t):this.initSelection.call(null,this.$element,function(e){n._isInitialized=!0,i.isArray(e)||(e=[e]),t(e)})},e}),e.define("select2/compat/inputData",["jquery","../utils"],function(s,i){function e(e,t,n){this._currentData=[],this._valueSeparator=n.get("valueSeparator")||",","hidden"===t.prop("type")&&n.get("debug")&&console&&console.warn&&console.warn("Select2: Using a hidden input with Select2 is no longer supported and may stop working in the future. It is recommended to use a `<select>` element instead."),e.call(this,t,n)}return e.prototype.current=function(e,t){function i(e,t){var n=[];return e.selected||-1!==s.inArray(e.id,t)?(e.selected=!0,n.push(e)):e.selected=!1,e.children&&n.push.apply(n,i(e.children,t)),n}for(var n=[],r=0;r<this._currentData.length;r++){var o=this._currentData[r];n.push.apply(n,i(o,this.$element.val().split(this._valueSeparator)))}t(n)},e.prototype.select=function(e,t){if(this.options.get("multiple")){var n=this.$element.val();n+=this._valueSeparator+t.id,this.$element.val(n),this.$element.trigger("change")}else this.current(function(e){s.map(e,function(e){e.selected=!1})}),this.$element.val(t.id),this.$element.trigger("change")},e.prototype.unselect=function(e,r){var o=this;r.selected=!1,this.current(function(e){for(var t=[],n=0;n<e.length;n++){var i=e[n];r.id!=i.id&&t.push(i.id)}o.$element.val(t.join(o._valueSeparator)),o.$element.trigger("change")})},e.prototype.query=function(e,t,n){for(var i=[],r=0;r<this._currentData.length;r++){var o=this._currentData[r],s=this.matches(t,o);null!==s&&i.push(s)}n({results:i})},e.prototype.addOptions=function(e,t){var n=s.map(t,function(e){return i.GetData(e[0],"data")});this._currentData.push.apply(this._currentData,n)},e}),e.define("select2/compat/matcher",["jquery"],function(s){return function(o){return function(e,t){var n=s.extend(!0,{},t);if(null==e.term||""===s.trim(e.term))return n;if(t.children){for(var i=t.children.length-1;0<=i;i--){var r=t.children[i];o(e.term,r.text,r)||n.children.splice(i,1)}if(0<n.children.length)return n}return o(e.term,t.text,t)?n:null}}}),e.define("select2/compat/query",[],function(){function e(e,t,n){n.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `query` option has been deprecated in favor of a custom data adapter that overrides the `query` method. Support will be removed for the `query` option in future versions of Select2."),e.call(this,t,n)}return e.prototype.query=function(e,t,n){t.callback=n,this.options.get("query").call(null,t)},e}),e.define("select2/dropdown/attachContainer",[],function(){function e(e,t,n){e.call(this,t,n)}return e.prototype.position=function(e,t,n){n.find(".dropdown-wrapper").append(t),t.addClass("select2-dropdown--below"),n.addClass("select2-container--below")},e}),e.define("select2/dropdown/stopPropagation",[],function(){function e(){}return e.prototype.bind=function(e,t,n){e.call(this,t,n);this.$dropdown.on(["blur","change","click","dblclick","focus","focusin","focusout","input","keydown","keyup","keypress","mousedown","mouseenter","mouseleave","mousemove","mouseover","mouseup","search","touchend","touchstart"].join(" "),function(e){e.stopPropagation()})},e}),e.define("select2/selection/stopPropagation",[],function(){function e(){}return e.prototype.bind=function(e,t,n){e.call(this,t,n);this.$selection.on(["blur","change","click","dblclick","focus","focusin","focusout","input","keydown","keyup","keypress","mousedown","mouseenter","mouseleave","mousemove","mouseover","mouseup","search","touchend","touchstart"].join(" "),function(e){e.stopPropagation()})},e}),l=function(p){var h,f,e=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],t="onwheel"in document||9<=document.documentMode?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],g=Array.prototype.slice;if(p.event.fixHooks)for(var n=e.length;n;)p.event.fixHooks[e[--n]]=p.event.mouseHooks;var m=p.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var e=t.length;e;)this.addEventListener(t[--e],i,!1);else this.onmousewheel=i;p.data(this,"mousewheel-line-height",m.getLineHeight(this)),p.data(this,"mousewheel-page-height",m.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var e=t.length;e;)this.removeEventListener(t[--e],i,!1);else this.onmousewheel=null;p.removeData(this,"mousewheel-line-height"),p.removeData(this,"mousewheel-page-height")},getLineHeight:function(e){var t=p(e),n=t["offsetParent"in p.fn?"offsetParent":"parent"]();return n.length||(n=p("body")),parseInt(n.css("fontSize"),10)||parseInt(t.css("fontSize"),10)||16},getPageHeight:function(e){return p(e).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};function i(e){var t,n=e||window.event,i=g.call(arguments,1),r=0,o=0,s=0,a=0,l=0;if((e=p.event.fix(n)).type="mousewheel","detail"in n&&(s=-1*n.detail),"wheelDelta"in n&&(s=n.wheelDelta),"wheelDeltaY"in n&&(s=n.wheelDeltaY),"wheelDeltaX"in n&&(o=-1*n.wheelDeltaX),"axis"in n&&n.axis===n.HORIZONTAL_AXIS&&(o=-1*s,s=0),r=0===s?o:s,"deltaY"in n&&(r=s=-1*n.deltaY),"deltaX"in n&&(o=n.deltaX,0===s&&(r=-1*o)),0!==s||0!==o){if(1===n.deltaMode){var c=p.data(this,"mousewheel-line-height");r*=c,s*=c,o*=c}else if(2===n.deltaMode){var u=p.data(this,"mousewheel-page-height");r*=u,s*=u,o*=u}if(t=Math.max(Math.abs(s),Math.abs(o)),(!f||t<f)&&y(n,f=t)&&(f/=40),y(n,t)&&(r/=40,o/=40,s/=40),r=Math[1<=r?"floor":"ceil"](r/f),o=Math[1<=o?"floor":"ceil"](o/f),s=Math[1<=s?"floor":"ceil"](s/f),m.settings.normalizeOffset&&this.getBoundingClientRect){var d=this.getBoundingClientRect();a=e.clientX-d.left,l=e.clientY-d.top}return e.deltaX=o,e.deltaY=s,e.deltaFactor=f,e.offsetX=a,e.offsetY=l,e.deltaMode=0,i.unshift(e,r,o,s),h&&clearTimeout(h),h=setTimeout(v,200),(p.event.dispatch||p.event.handle).apply(this,i)}}function v(){f=null}function y(e,t){return m.settings.adjustOldDeltas&&"mousewheel"===e.type&&t%120==0}p.fn.extend({mousewheel:function(e){return e?this.bind("mousewheel",e):this.trigger("mousewheel")},unmousewheel:function(e){return this.unbind("mousewheel",e)}})},"function"==typeof e.define&&e.define.amd?e.define("jquery-mousewheel",["jquery"],l):"object"==typeof exports?module.exports=l:l(d),e.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults","./select2/utils"],function(r,e,o,t,s){if(null==r.fn.select2){var a=["open","close","destroy"];r.fn.select2=function(t){if("object"==typeof(t=t||{}))return this.each(function(){var e=r.extend(!0,{},t);new o(r(this),e)}),this;if("string"!=typeof t)throw new Error("Invalid arguments for Select2: "+t);var n,i=Array.prototype.slice.call(arguments,1);return this.each(function(){var e=s.GetData(this,"select2");null==e&&window.console&&console.error&&console.error("The select2('"+t+"') method was called on an element that is not using Select2."),n=e[t].apply(e,i)}),-1<r.inArray(t,a)?this:n}}return null==r.fn.select2.defaults&&(r.fn.select2.defaults=t),o}),{define:e.define,require:e.require}}(),t=e.require("jquery.select2");return d.fn.select2.amd=e,t});;
// ----------------------------------------------------------------------------
// markItUp! Universal MarkUp Engine, JQuery plugin
// v 1.1.x
// Dual licensed under the MIT and GPL licenses.
// ----------------------------------------------------------------------------
// Copyright (C) 2007-2010 Jay Salvat
// http://markitup.jaysalvat.com/
// ----------------------------------------------------------------------------
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// ----------------------------------------------------------------------------
(function ($) {
    $.fn.markItUp = function (settings, extraSettings) {
        var options, ctrlKey, shiftKey, altKey;
        ctrlKey = shiftKey = altKey = false;

        options = {
            id: '',
            nameSpace: '',
            root: '',
            previewInWindow: '', // 'width=800, height=600, resizable=yes, scrollbars=yes'
            previewAutoRefresh: true,
            previewPosition: 'after',
            previewTemplatePath: '~/templates/preview.html',
            previewParserPath: '',
            previewParserVar: 'data',
            resizeHandle: true,
            beforeInsert: '',
            afterInsert: '',
            onEnter: {},
            onShiftEnter: {},
            onCtrlEnter: {},
            onTab: {},
            markupSet: [{ /* set */ }]
        };
        $.extend(options, settings, extraSettings);

        // compute markItUp! path
        if (!options.root) {
            $('script').each(function (a, tag) {
                miuScript = $(tag).get(0).src.match(/(.*)jquery\.markitup(\.pack)?\.js$/);
                if (miuScript !== null) {
                    options.root = miuScript[1];
                }
            });
        }

        return this.each(function () {
            var $$, textarea, levels, scrollPosition, caretPosition, caretOffset,
				clicked, hash, header, footer, previewWindow, template, iFrame, abort;
            $$ = $(this);
            textarea = this;
            levels = [];
            abort = false;
            scrollPosition = caretPosition = 0;
            caretOffset = -1;

            options.previewParserPath = localize(options.previewParserPath);
            options.previewTemplatePath = localize(options.previewTemplatePath);

            // apply the computed path to ~/
            function localize(data, inText) {
                if (inText) {
                    return data.replace(/("|')~\//g, "$1" + options.root);
                }
                return data.replace(/^~\//, options.root);
            }

            // init and build editor
            function init() {
                id = ''; nameSpace = '';
                if (options.id) {
                    id = 'id="' + options.id + '"';
                } else if ($$.attr("id")) {
                    id = 'id="markItUp' + ($$.attr("id").substr(0, 1).toUpperCase()) + ($$.attr("id").substr(1)) + '"';

                }
                if (options.nameSpace) {
                    nameSpace = 'class="' + options.nameSpace + '"';
                }
                $$.wrap('<div ' + nameSpace + '></div>');
                $$.wrap('<div ' + id + ' class="markItUp"></div>');
                $$.wrap('<div class="markItUpContainer"></div>');
                $$.addClass("markItUpEditor");

                // add the header before the textarea
                header = $('<div class="markItUpHeader"></div>').insertBefore($$);
                $(dropMenus(options.markupSet)).appendTo(header);

                // add the footer after the textarea
                footer = $('<div class="markItUpFooter"></div>').insertAfter($$);

                // add the resize handle after textarea
                if (options.resizeHandle === true && $.browser.safari !== true) {
                    resizeHandle = $('<div class="markItUpResizeHandle"></div>')
						.insertAfter($$)
						.bind("mousedown", function (e) {
						    var h = $$.height(), y = e.clientY, mouseMove, mouseUp;
						    mouseMove = function (e) {
						        $$.css("height", Math.max(20, e.clientY + h - y) + "px");
						        return false;
						    };
						    mouseUp = function (e) {
						        $("html").unbind("mousemove", mouseMove).unbind("mouseup", mouseUp);
						        return false;
						    };
						    $("html").bind("mousemove", mouseMove).bind("mouseup", mouseUp);
						});
                    footer.append(resizeHandle);
                }

                // listen key events
                $$.keydown(keyPressed).keyup(keyPressed);

                // bind an event to catch external calls
                $$.bind("insertion", function (e, settings) {
                    if (settings.target !== false) {
                        get();
                    }
                    if (textarea === $.markItUp.focused) {
                        markup(settings);
                    }
                });

                // remember the last focus
                $$.focus(function () {
                    $.markItUp.focused = this;
                });
            }

            // recursively build header with dropMenus from markupset
            function dropMenus(markupSet) {
                var ul = $('<ul></ul>'), i = 0;
                $('li:hover > ul', ul).css('display', 'block');
                $.each(markupSet, function () {
                    var button = this, t = '', title, li, j;
                    title = (button.key) ? (button.name || '') + ' [Ctrl+' + button.key + ']' : (button.name || '');
                    key = (button.key) ? 'accesskey="' + button.key + '"' : '';
                    if (button.separator) {
                        li = $('<li class="markItUpSeparator">' + (button.separator || '') + '</li>').appendTo(ul);
                    } else {
                        i++;
                        for (j = levels.length - 1; j >= 0; j--) {
                            t += levels[j] + "-";
                        }
                        li = $('<li class="markItUpButton markItUpButton' + t + (i) + ' ' + (button.className || '') + '"><a href="" ' + key + ' title="' + title + '"><i class="' + (button.iconClass || button.name || '') + '"></i></a></li>')
						.bind("contextmenu", function () { // prevent contextmenu on mac and allow ctrl+click
						    return false;
						}).click(function () {
						    return false;
						}).bind("focusin", function () {
						    $$.focus();
						}).mousedown(function () {
						    if (button.call) {
						        eval(button.call)();
						    }
						    setTimeout(function () { markup(button) }, 1);
						    return false;
						}).hover(function () {
						    $('> ul', this).show();
						    $(document).one('click', function () { // close dropmenu if click outside
						        $('ul ul', header).hide();
						    }
                            );
						}, function () {
						    $('> ul', this).hide();
						}
						).appendTo(ul);
                        if (button.dropMenu) {
                            levels.push(i);
                            $(li).addClass('markItUpDropMenu').append(dropMenus(button.dropMenu));
                        }
                    }
                });
                levels.pop();
                return ul;
            }

            // markItUp! markups
            function magicMarkups(string) {
                if (string) {
                    string = string.toString();
                    string = string.replace(/\(\!\(([\s\S]*?)\)\!\)/g,
						function (x, a) {
						    var b = a.split('|!|');
						    if (altKey === true) {
						        return (b[1] !== undefined) ? b[1] : b[0];
						    } else {
						        return (b[1] === undefined) ? "" : b[0];
						    }
						}
					);
                    // [![prompt]!], [![prompt:!:value]!]
                    string = string.replace(/\[\!\[([\s\S]*?)\]\!\]/g,
						function (x, a) {
						    var b = a.split(':!:');
						    if (abort === true) {
						        return false;
						    }
						    value = prompt(b[0], (b[1]) ? b[1] : '');
						    if (value === null) {
						        abort = true;
						    }
						    return value;
						}
					);
                    return string;
                }
                return "";
            }

            // prepare action
            function prepare(action) {
                if ($.isFunction(action)) {
                    action = action(hash);
                }
                return magicMarkups(action);
            }

            // build block to insert
            function build(string) {
                var openWith = prepare(clicked.openWith);
                var placeHolder = prepare(clicked.placeHolder);
                var replaceWith = prepare(clicked.replaceWith);
                var closeWith = prepare(clicked.closeWith);
                if (replaceWith !== "") {
                    block = openWith + replaceWith + closeWith;
                } else if (selection === '' && placeHolder !== '') {
                    block = openWith + placeHolder + closeWith;
                } else {
                    string = string || selection;
                    if (string.match(/ $/)) {
                        block = openWith + string.replace(/ $/, '') + closeWith + ' ';
                    } else {
                        block = openWith + string + closeWith;
                    }
                }
                return {
                    block: block,
                    openWith: openWith,
                    replaceWith: replaceWith,
                    placeHolder: placeHolder,
                    closeWith: closeWith
                };
            }

            // define markup to insert
            function markup(button) {
                var len, j, n, i;
                hash = clicked = button;
                get();

                $.extend(hash, {
                    line: "",
                    root: options.root,
                    textarea: textarea,
                    selection: (selection || ''),
                    caretPosition: caretPosition,
                    ctrlKey: ctrlKey,
                    shiftKey: shiftKey,
                    altKey: altKey
                }
							);
                // callbacks before insertion
                prepare(options.beforeInsert);
                prepare(clicked.beforeInsert);
                if (ctrlKey === true && shiftKey === true) {
                    prepare(clicked.beforeMultiInsert);
                }
                $.extend(hash, { line: 1 });

                if (ctrlKey === true && shiftKey === true) {
                    lines = selection.split(/\r?\n/);
                    for (j = 0, n = lines.length, i = 0; i < n; i++) {
                        if ($.trim(lines[i]) !== '') {
                            $.extend(hash, { line: ++j, selection: lines[i] });
                            lines[i] = build(lines[i]).block;
                        } else {
                            lines[i] = "";
                        }
                    }
                    string = { block: lines.join('\n') };
                    start = caretPosition;
                    len = string.block.length + (($.browser.opera) ? n - 1 : 0);
                } else if (ctrlKey === true) {
                    string = build(selection);
                    start = caretPosition + string.openWith.length;
                    len = string.block.length - string.openWith.length - string.closeWith.length;
                    len = len - (string.block.match(/ $/) ? 1 : 0);
                    len -= fixIeBug(string.block);
                } else if (shiftKey === true) {
                    string = build(selection);
                    start = caretPosition;
                    len = string.block.length;
                    len -= fixIeBug(string.block);
                } else {
                    string = build(selection);
                    start = caretPosition + string.block.length;
                    len = 0;
                    start -= fixIeBug(string.block);
                }
                if ((selection === '' && string.replaceWith === '')) {
                    caretOffset += fixOperaBug(string.block);

                    start = caretPosition + string.openWith.length;
                    len = string.block.length - string.openWith.length - string.closeWith.length;

                    caretOffset = $$.val().substring(caretPosition, $$.val().length).length;
                    caretOffset -= fixOperaBug($$.val().substring(0, caretPosition));
                }
                $.extend(hash, { caretPosition: caretPosition, scrollPosition: scrollPosition });

                if (string.block !== selection && abort === false) {
                    insert(string.block);
                    set(start, len);
                } else {
                    caretOffset = -1;
                }
                get();

                $.extend(hash, { line: '', selection: selection });

                // callbacks after insertion
                if (ctrlKey === true && shiftKey === true) {
                    prepare(clicked.afterMultiInsert);
                }
                prepare(clicked.afterInsert);
                prepare(options.afterInsert);

                // refresh preview if opened
                if (previewWindow && options.previewAutoRefresh) {
                    refreshPreview();
                }

                // reinit keyevent
                shiftKey = altKey = ctrlKey = abort = false;
            }

            // Substract linefeed in Opera
            function fixOperaBug(string) {
                if ($.browser.opera) {
                    return string.length - string.replace(/\n*/g, '').length;
                }
                return 0;
            }
            // Substract linefeed in IE
            function fixIeBug(string) {
                if ($.browser.msie) {
                    return string.length - string.replace(/\r/g, '').length;
                }
                return 0;
            }

            // add markup
            function insert(block) {
                if (document.selection) {
                    var newSelection = document.selection.createRange();
                    newSelection.text = block;
                } else {
                    textarea.value = textarea.value.substring(0, caretPosition) + block + textarea.value.substring(caretPosition + selection.length, textarea.value.length);
                }
            }

            // set a selection
            function set(start, len) {
                if (textarea.createTextRange) {
                    // quick fix to make it work on Opera 9.5
                    if ($.browser.opera && $.browser.version >= 9.5 && len == 0) {
                        return false;
                    }
                    range = textarea.createTextRange();
                    range.collapse(true);
                    range.moveStart('character', start);
                    range.moveEnd('character', len);
                    range.select();
                } else if (textarea.setSelectionRange) {
                    textarea.setSelectionRange(start, start + len);
                }
                textarea.scrollTop = scrollPosition;
                textarea.focus();
            }

            // get the selection
            function get() {
                textarea.focus();

                scrollPosition = textarea.scrollTop;
                if (document.selection) {
                    selection = document.selection;
                    if ($.browser.msie) { // ie	
                        var range = selection.createRange();
                        var stored_range = range.duplicate();
                        stored_range.moveToElementText(textarea);
                        stored_range.setEndPoint('EndToEnd', range);
                        var s = stored_range.text.length - range.text.length;

                        caretPosition = s - (textarea.value.substr(0, s).length - textarea.value.substr(0, s).replace(/\r/g, '').length);
                        selection = range.text;
                    } else { // opera
                        caretPosition = textarea.selectionStart;
                    }
                } else { // gecko & webkit
                    caretPosition = textarea.selectionStart;
                    selection = textarea.value.substring(caretPosition, textarea.selectionEnd);
                }
                return selection;
            }

            // open preview window
            function preview() {
                if (!previewWindow || previewWindow.closed) {
                    if (options.previewInWindow) {
                        previewWindow = window.open('', 'preview', options.previewInWindow);
                        $(window).unload(function () {
                            previewWindow.close();
                        });
                    } else {
                        iFrame = $('<iframe class="markItUpPreviewFrame"></iframe>');
                        //iFrame = $('<div class="markItUpPreviewFrame"></div>');
                        if (options.previewPosition == 'after') {
                            iFrame.insertAfter(footer);
                        } else {
                            iFrame.insertBefore(header);
                        }
                        previewWindow = iFrame[iFrame.length - 1].contentWindow || frame[iFrame.length - 1];
                    }
                } else if (altKey === true) {
                    if (iFrame) {
                        iFrame.remove();
                    } else {
                        previewWindow.close();
                    }
                    previewWindow = iFrame = false;
                }
                if (!options.previewAutoRefresh) {
                    refreshPreview();
                }
                if (options.previewInWindow) {
                    previewWindow.focus();
                }
            }

            // refresh Preview window
            function refreshPreview() {
                renderPreview();
            }

            function renderPreview() {
                var phtml;
                if (options.previewParserPath !== '') {
                    $.ajax({
                        type: 'POST',
                        dataType: 'text',
                        global: false,
                        url: options.previewParserPath,
                        data: options.previewParserVar + '=' + encodeURIComponent($$.val()),
                        success: function (data) {
                            writeInPreview(localize(data, 1));
                        }
                    });
                } else {
                    if (!template) {
                        $.ajax({
                            url: options.previewTemplatePath,
                            dataType: 'text',
                            global: false,
                            success: function (data) {
                                writeInPreview(localize(data, 1).replace(/<!-- content -->/g, $$.val()));
                            }
                        });
                    }
                }
                return false;
            }

            function writeInPreview(data) {
                if (previewWindow.document) {
                    try {
                        sp = previewWindow.document.documentElement.scrollTop
                    } catch (e) {
                        sp = 0;
                    }
                    previewWindow.document.open();
                    previewWindow.document.write(data);
                    previewWindow.document.close();
                    previewWindow.document.documentElement.scrollTop = sp;
                }
            }

            // set keys pressed
            function keyPressed(e) {
                shiftKey = e.shiftKey;
                altKey = e.altKey;
                ctrlKey = (!(e.altKey && e.ctrlKey)) ? e.ctrlKey : false;

                if (e.type === 'keydown') {
                    if (ctrlKey === true) {
                        li = $("a[accesskey=" + String.fromCharCode(e.keyCode) + "]", header).parent('li');
                        if (li.length !== 0) {
                            ctrlKey = false;
                            setTimeout(function () {
                                li.triggerHandler('mousedown');
                            }, 1);
                            return false;
                        }
                    }
                    if (e.keyCode === 13 || e.keyCode === 10) { // Enter key
                        if (ctrlKey === true) {  // Enter + Ctrl
                            ctrlKey = false;
                            markup(options.onCtrlEnter);
                            return options.onCtrlEnter.keepDefault;
                        } else if (shiftKey === true) { // Enter + Shift
                            shiftKey = false;
                            markup(options.onShiftEnter);
                            return options.onShiftEnter.keepDefault;
                        } else { // only Enter
                            markup(options.onEnter);
                            return options.onEnter.keepDefault;
                        }
                    }
                    if (e.keyCode === 9) { // Tab key
                        if (shiftKey == true || ctrlKey == true || altKey == true) {
                            return false;
                        }
                        if (caretOffset !== -1) {
                            get();
                            caretOffset = $$.val().length - caretOffset;
                            set(caretOffset, 0);
                            caretOffset = -1;
                            return false;
                        } else {
                            markup(options.onTab);
                            return options.onTab.keepDefault;
                        }
                    }
                }
            }

            init();
        });
    };

    $.fn.markItUpRemove = function () {
        return this.each(function () {
            var $$ = $(this).unbind().removeClass('markItUpEditor');
            $$.parent('div').parent('div.markItUp').parent('div').replaceWith($$);
        }
		);
    };

    $.markItUp = function (settings) {
        var options = { target: false };
        $.extend(options, settings);
        if (options.target) {
            return $(options.target).each(function () {
                $(this).focus();
                $(this).trigger('insertion', [options]);
            });
        } else {
            $('textarea').trigger('insertion', [options]);
        }
    };
})(jQuery);
;
// ----------------------------------------------------------------------------
// markItUp! Custom settings by Forzyy (mania-exchange.com)
// ----------------------------------------------------------------------------
mySettings = {
	previewParserPath: '/api/bbcode', // path to your BBCode parser
	resizeHandle: false,
	markupSet: [
		{ name: 'Bold', iconClass: 'fas fa-bold', key: 'B', openWith: '[b]', closeWith: '[/b]' },
		{ name: 'Italic', iconClass: 'fas fa-italic', key: 'I', openWith: '[i]', closeWith: '[/i]' },
		{ name: 'Underline', iconClass: 'fas fa-underline', key: 'U', openWith: '[u]', closeWith: '[/u]' },
		{ name: 'Strike', iconClass: 'fas fa-strikethrough', key: 'S', openWith: '[s]', closeWith: '[/s]' },
		{ separator: '---------------' },
		{ name: 'Picture', iconClass: 'fas fa-image', key: 'P', replaceWith: '[img][![Url]!][/img]' },
		{ name: 'YouTube video', iconClass: 'fab fa-youtube', key: 'Q', replaceWith: '[youtube][![YouTube url]!][/youtube]' },
		{ name: 'Link', iconClass: 'fas fa-link', key: 'L', openWith: '[url=[![Url]!]]', closeWith: '[/url]', placeHolder: 'Your text to link here...' },
		{ separator: '---------------' },
		{ name: 'Quotes', iconClass: 'fas fa-quote-left', openWith: '[quote]', closeWith: '[/quote]' },
		{ name: 'Code', iconClass: 'fas fa-code', openWith: '[code]', closeWith: '[/code]' },
		{ name: 'Horizontal line', iconClass: 'fas fa-grip-lines', openWith: '[hr]', closeWith: '' },
		{ separator: '---------------' },
		{ name: 'List', iconClass: 'fas fa-list-ul', openWith: '[list]\n', closeWith: '\n[/list]' },
		{ name: 'List Item', iconClass: 'fas fa-circle', openWith: '[item] ' },
		{ separator: '---------------' },
		{ name: 'Align left', iconClass: 'fas fa-align-left', openWith: '[align=left]', closeWith: '[/align]' },
        { name: 'Align center', iconClass: 'fas fa-align-center', openWith: '[align=center]', closeWith: '[/align]' },
        { name: 'Align right', iconClass: 'fas fa-align-right', openWith: '[align=right]', closeWith: '[/align]' },
		{ separator: '---------------' },
		{ name: 'Link to MX user', iconClass: 'fas fa-user', replaceWith: '[user][![MX user id]!][/user]' },
		{ name: 'Link to MX map', iconClass: 'fas fa-route', replaceWith: '[map][![MX map id]!][/map]' },
        { name: 'Link to MX map (map name only)', iconClass: 'fas fa-map-marked-alt', replaceWith: '[map=short][![MX map id]!][/map]' },
		{ name: 'Link to MX map (also show map screenshot)', iconClass: 'fas fa-image', replaceWith: '[map=full][![MX map id]!][/map]' },
        { name: 'Fancy MX text', iconClass: 'fas fa-heart', openWith: (window.location.href.indexOf("trackmania.exchange") > -1 ? '[tmx]' : '[mx]'), closeWith: '' },
		{ separator: '---------------' },
		{ name: 'Clean', iconClass: 'fas fa-eraser', className: "clean", replaceWith: function (markitup) { return markitup.selection.replace(/\[(.*?)\]/g, "") } }
	]
}
mySettings2 = {
    previewParserPath: '/api/bbcode', // path to your BBCode parser
    resizeHandle: false,
    markupSet: [
        { name: 'Bold', iconClass: 'fas fa-bold', key: 'B', openWith: '[b]', closeWith: '[/b]' },
        { name: 'Italic', iconClass: 'fas fa-italic', key: 'I', openWith: '[i]', closeWith: '[/i]' },
        { name: 'Underline', iconClass: 'fas fa-underline', key: 'U', openWith: '[u]', closeWith: '[/u]' },
        { name: 'Strike', iconClass: 'fas fa-strikethrough', key: 'S', openWith: '[s]', closeWith: '[/s]' },
        { separator: '---------------' },
        { name: 'Picture', iconClass: 'fas fa-image', key: 'P', replaceWith: '[img][![Url]!][/img]' },
        { name: 'YouTube video', iconClass: 'fab fa-youtube', key: 'Q', replaceWith: '[youtube][![YouTube url]!][/youtube]' },
        { name: 'Link', iconClass: 'fas fa-link', key: 'L', openWith: '[url=[![Url]!]]', closeWith: '[/url]', placeHolder: 'Your text to link here...' },
        { separator: '---------------' },
        { name: 'Quotes', iconClass: 'fas fa-quote-left', openWith: '[quote]', closeWith: '[/quote]' },
        { name: 'Code', iconClass: 'fas fa-code', openWith: '[code]', closeWith: '[/code]' },
        { name: 'Horizontal line', iconClass: 'fas fa-grip-lines', openWith: '[hr]', closeWith: '' },
        { separator: '---------------' },
        { name: 'List', iconClass: 'fas fa-list-ul', openWith: '[list]\n', closeWith: '\n[/list]' },
        { name: 'List Item', iconClass: 'fas fa-circle', openWith: '[item] ' },
        { separator: '---------------' },
        { name: 'Align left', iconClass: 'fas fa-align-left', openWith: '[align=left]', closeWith: '[/align]' },
        { name: 'Align center', iconClass: 'fas fa-align-center', openWith: '[align=center]', closeWith: '[/align]' },
        { name: 'Align right', iconClass: 'fas fa-align-right', openWith: '[align=right]', closeWith: '[/align]' },
        { separator: '---------------' },
        { name: 'Link to MX user', iconClass: 'fas fa-user', replaceWith: '[user][![MX user id]!][/user]' },
        { name: 'Link to MX map', iconClass: 'fas fa-route', replaceWith: '[map][![MX map id]!][/map]' },
        { name: 'Link to MX map (map name only)', iconClass: 'fas fa-map-marked-alt', replaceWith: '[map=short][![MX map id]!][/map]' },
        { name: 'Link to MX map (also show map screenshot)', iconClass: 'fas fa-image', replaceWith: '[map=full][![MX map id]!][/map]' },
        { name: 'Fancy MX text', iconClass: 'fas fa-heart', openWith: (window.location.href.indexOf("trackmania.exchange") > -1 ? '[tmx]' : '[mx]'), closeWith: '' }
    ]
}
noThing = {
    previewParserPath: '/api/bbcode', // path to your BBCode parser
    resizeHandle: false,
    markupSet: [
        { name: 'Bold', iconClass: 'fas fa-bold', key: 'B', openWith: '[b]', closeWith: '[/b]' },
        { name: 'Italic', iconClass: 'fas fa-italic', key: 'I', openWith: '[i]', closeWith: '[/i]' },
        { name: 'Underline', iconClass: 'fas fa-underline', key: 'U', openWith: '[u]', closeWith: '[/u]' },
        { name: 'Strike', iconClass: 'fas fa-strikethrough', key: 'S', openWith: '[s]', closeWith: '[/s]' },
        { separator: '---------------' },
        { name: 'Link', iconClass: 'fas fa-link', key: 'L', openWith: '[url=[![Url]!]]', closeWith: '[/url]', placeHolder: 'Your text to link here...' },
        { separator: '---------------' },
        { name: 'Quotes', iconClass: 'fas fa-quote-left', openWith: '[quote]', closeWith: '[/quote]' },
        { name: 'Code', iconClass: 'fas fa-code', openWith: '[code]', closeWith: '[/code]' },
        { name: 'Horizontal line', iconClass: 'fas fa-grip-lines', openWith: '[hr]', closeWith: '' },
        { separator: '---------------' },
        { name: 'List', iconClass: 'fas fa-list-ul', openWith: '[list]\n', closeWith: '\n[/list]' },
        { name: 'List Item', iconClass: 'fas fa-circle', openWith: '[item] ' },
        { separator: '---------------' },
        { name: 'Align left', iconClass: 'fas fa-align-left', openWith: '[align=left]', closeWith: '[/align]' },
        { name: 'Align center', iconClass: 'fas fa-align-center', openWith: '[align=center]', closeWith: '[/align]' },
        { name: 'Align right', iconClass: 'fas fa-align-right', openWith: '[align=right]', closeWith: '[/align]' },
        { separator: '---------------' },
        { name: 'Link to MX user', iconClass: 'fas fa-user', replaceWith: '[user][![MX user id]!][/user]' },
        { name: 'Link to MX map', iconClass: 'fas fa-route', replaceWith: '[map][![MX map id]!][/map]' },
        { name: 'Link to MX map (map name only)', iconClass: 'fas fa-map-marked-alt', replaceWith: '[map=short][![MX map id]!][/map]' },
        { name: 'Link to MX map (also show map screenshot)', iconClass: 'fas fa-image', replaceWith: '[map=full][![MX map id]!][/map]' },
        { name: 'Fancy MX text', iconClass: 'fas fa-heart', openWith: (window.location.href.indexOf("trackmania.exchange") > -1 ? '[tmx]' : '[mx]'), closeWith: '' },
        { separator: '---------------' },
        { name: 'Clean', iconClass: 'fas fa-eraser', className: "clean", replaceWith: function (markitup) { return markitup.selection.replace(/\[(.*?)\]/g, "") } }
    ]
}

noImages = {
    previewParserPath: '/api/bbcode', // path to your BBCode parser
    resizeHandle: false,
    markupSet: [
        { name: 'Bold', iconClass: 'fas fa-bold', key: 'B', openWith: '[b]', closeWith: '[/b]' },
        { name: 'Italic', iconClass: 'fas fa-italic', key: 'I', openWith: '[i]', closeWith: '[/i]' },
        { name: 'Underline', iconClass: 'fas fa-underline', key: 'U', openWith: '[u]', closeWith: '[/u]' },
        { name: 'Strike', iconClass: 'fas fa-strikethrough', key: 'S', openWith: '[s]', closeWith: '[/s]' },
        { separator: '---------------' },
        { name: 'YouTube video', iconClass: 'fab fa-youtube', key: 'Q', replaceWith: '[youtube][![YouTube url]!][/youtube]' },
        { name: 'Link', iconClass: 'fas fa-link', key: 'L', openWith: '[url=[![Url]!]]', closeWith: '[/url]', placeHolder: 'Your text to link here...' },
        { separator: '---------------' },
        { name: 'Quotes', iconClass: 'fas fa-quote-left', openWith: '[quote]', closeWith: '[/quote]' },
        { name: 'Code', iconClass: 'fas fa-code', openWith: '[code]', closeWith: '[/code]' },
        { name: 'Horizontal line', iconClass: 'fas fa-grip-lines', openWith: '[hr]', closeWith: '' },
        { separator: '---------------' },
        { name: 'List', iconClass: 'fas fa-list-ul', openWith: '[list]\n', closeWith: '\n[/list]' },
        { name: 'List Item', iconClass: 'fas fa-circle', openWith: '[item] ' },
        { separator: '---------------' },
        { name: 'Align left', iconClass: 'fas fa-align-left', openWith: '[align=left]', closeWith: '[/align]' },
        { name: 'Align center', iconClass: 'fas fa-align-center', openWith: '[align=center]', closeWith: '[/align]' },
        { name: 'Align right', iconClass: 'fas fa-align-right', openWith: '[align=right]', closeWith: '[/align]' },
        { separator: '---------------' },
        { name: 'Link to MX user', iconClass: 'fas fa-user', replaceWith: '[user][![MX user id]!][/user]' },
        { name: 'Link to MX map', iconClass: 'fas fa-route', replaceWith: '[map][![MX map id]!][/map]' },
        { name: 'Link to MX map (map name only)', iconClass: 'fas fa-map-marked-alt', replaceWith: '[map=short][![MX map id]!][/map]' },
        { name: 'Link to MX map (also show map screenshot)', iconClass: 'fas fa-image', replaceWith: '[map=full][![MX map id]!][/map]' },
        { name: 'Fancy MX text', iconClass: 'fas fa-heart', openWith: (window.location.href.indexOf("trackmania.exchange") > -1 ? '[tmx]' : '[mx]'), closeWith: '' },
        { separator: '---------------' },
        { name: 'Clean', iconClass: 'fas fa-eraser', className: "clean", replaceWith: function (markitup) { return markitup.selection.replace(/\[(.*?)\]/g, "") } }
    ]
};
/* Initialize JS here */
$(function () {
	// Configure tipsy for east, south and north.
    $('.tipsy-e').tipsy({ live: true, gravity: 'e', fade: true, delayIn: 10 });
    $('.tipsy-e-html').tipsy({ live: true, gravity: 'e', fade: true, delayIn: 10, html: true });
    $('.tipsy-w-html').tipsy({ live: true, gravity: 'w', fade: true, delayIn: 10, html: true });
	$('.tipsy-s').tipsy({ live: true, gravity: 's', fade: true, delayIn: 10 });
    $('.tipsy-s-html').tipsy({ live: true, gravity: 's', fade: true, delayIn: 10, html: true });
    $('.tipsy-n-html').tipsy({ live: true, gravity: 'n', fade: true, delayIn: 10, html: true });


    $('[data-toggle="tooltip"]').tooltip();
	// Load the track info boxes.
	//mxc.loadTrackInfoBoxes();
});;
(function($){$.jGrowl=function(m,o){if($('#jGrowl').size()==0)
$('<div id="jGrowl"></div>').addClass($.jGrowl.defaults.position).appendTo('body');$('#jGrowl').jGrowl(m,o);};$.fn.jGrowl=function(m,o){if($.isFunction(this.each)){var args=arguments;return this.each(function(){var self=this;if($(this).data('jGrowl.instance')==undefined){$(this).data('jGrowl.instance',$.extend(new $.fn.jGrowl(),{notifications:[],element:null,interval:null}));$(this).data('jGrowl.instance').startup(this);}
if($.isFunction($(this).data('jGrowl.instance')[m])){$(this).data('jGrowl.instance')[m].apply($(this).data('jGrowl.instance'),$.makeArray(args).slice(1));}else{$(this).data('jGrowl.instance').create(m,o);}});};};$.extend($.fn.jGrowl.prototype,{defaults:{pool:0,header:'',group:'',sticky:false,position:'top-right',glue:'after',theme:'default',corners:'10px',check:250,life:3000,speed:'normal',easing:'swing',closer:true,closeTemplate:'&times;',closerTemplate:'<div>[ close all ]</div>',log:function(e,m,o){},beforeOpen:function(e,m,o){},open:function(e,m,o){},beforeClose:function(e,m,o){},close:function(e,m,o){},animateOpen:{opacity:'show'},animateClose:{opacity:'hide'}},notifications:[],element:null,interval:null,create:function(message,o){var o=$.extend({},this.defaults,o);this.notifications.push({message:message,options:o});o.log.apply(this.element,[this.element,message,o]);},render:function(notification){var self=this;var message=notification.message;var o=notification.options;var notification=$('<div class="jGrowl-notification ui-state-highlight ui-corner-all'+
((o.group!=undefined&&o.group!='')?' '+o.group:'')+'">'+'<div class="close" title="Close this notification.">'+o.closeTemplate+'</div>'+'<div class="header">'+o.header+'</div>'+'<div class="message">'+message+'</div></div>').data("jGrowl",o).addClass(o.theme).children('div.close').bind("click.jGrowl",function(){$(this).parent().trigger('jGrowl.close');}).parent();$(notification).bind("mouseover.jGrowl",function(){$('div.jGrowl-notification',self.element).data("jGrowl.pause",true);}).bind("mouseout.jGrowl",function(){$('div.jGrowl-notification',self.element).data("jGrowl.pause",false);}).bind('jGrowl.beforeOpen',function(){if(o.beforeOpen.apply(notification,[notification,message,o,self.element])!=false){$(this).trigger('jGrowl.open');}}).bind('jGrowl.open',function(){if(o.open.apply(notification,[notification,message,o,self.element])!=false){if(o.glue=='after'){$('div.jGrowl-notification:last',self.element).after(notification);}else{$('div.jGrowl-notification:first',self.element).before(notification);}
$(this).animate(o.animateOpen,o.speed,o.easing,function(){if($.browser.msie&&(parseInt($(this).css('opacity'),10)===1||parseInt($(this).css('opacity'),10)===0))
this.style.removeAttribute('filter');$(this).data("jGrowl").created=new Date();});}}).bind('jGrowl.beforeClose',function(){if(o.beforeClose.apply(notification,[notification,message,o,self.element])!=false)
$(this).trigger('jGrowl.close');}).bind('jGrowl.close',function(){$(this).data('jGrowl.pause',true);$(this).animate(o.animateClose,o.speed,o.easing,function(){$(this).remove();var close=o.close.apply(notification,[notification,message,o,self.element]);if($.isFunction(close))
close.apply(notification,[notification,message,o,self.element]);});}).trigger('jGrowl.beforeOpen');if($.fn.corner!=undefined)$(notification).corner(o.corners);if($('div.jGrowl-notification:parent',self.element).size()>1&&$('div.jGrowl-closer',self.element).size()==0&&this.defaults.closer!=false){$(this.defaults.closerTemplate).addClass('jGrowl-closer ui-state-highlight ui-corner-all').addClass(this.defaults.theme).appendTo(self.element).animate(this.defaults.animateOpen,this.defaults.speed,this.defaults.easing).bind("click.jGrowl",function(){$(this).siblings().children('div.close').trigger("click.jGrowl");if($.isFunction(self.defaults.closer)){self.defaults.closer.apply($(this).parent()[0],[$(this).parent()[0]]);}});};},update:function(){$(this.element).find('div.jGrowl-notification:parent').each(function(){if($(this).data("jGrowl")!=undefined&&$(this).data("jGrowl").created!=undefined&&($(this).data("jGrowl").created.getTime()+$(this).data("jGrowl").life)<(new Date()).getTime()&&$(this).data("jGrowl").sticky!=true&&($(this).data("jGrowl.pause")==undefined||$(this).data("jGrowl.pause")!=true)){$(this).trigger('jGrowl.beforeClose');}});if(this.notifications.length>0&&(this.defaults.pool==0||$(this.element).find('div.jGrowl-notification:parent').size()<this.defaults.pool))
this.render(this.notifications.shift());if($(this.element).find('div.jGrowl-notification:parent').size()<2){$(this.element).find('div.jGrowl-closer').animate(this.defaults.animateClose,this.defaults.speed,this.defaults.easing,function(){$(this).remove();});}},startup:function(e){this.element=$(e).addClass('jGrowl').append('<div class="jGrowl-notification"></div>');this.interval=setInterval(function(){$(e).data('jGrowl.instance').update();},this.defaults.check);if($.browser.msie&&parseInt($.browser.version)<7&&!window["XMLHttpRequest"]){$(this.element).addClass('ie6');}},shutdown:function(){$(this.element).removeClass('jGrowl').find('div.jGrowl-notification').remove();clearInterval(this.interval);},close:function(){$(this.element).find('div.jGrowl-notification').each(function(){$(this).trigger('jGrowl.beforeClose');});}});$.jGrowl.defaults=$.fn.jGrowl.prototype.defaults;})(jQuery);;

function jGrowlTheme(type, header, message, image) {
	
	switch(type) {
		case "mono": 
			
			var theText 		= /*'<img src="' + image + '" class="img-thumb" /><span class="separator">&nbsp;</span>' +*/ message;
	
			var themeAnimate = 	function() {
											setTimeout(function() { 
												$('img.img-thumb').animate({marginLeft: "-.5em"});
												$('.separator').animate({marginLeft: "-.3em"});
												$('div.jGrowl div.themed div.header').animate({marginLeft: "4.7em"}, 500);
												$('div.jGrowl div.themed div.message').animate({marginLeft: "6em"}, 1000);
											}, 10);
										}
					
			$.jGrowl(theText, {
						header: header,
						theme: 'themed', 
						open: themeAnimate,
						sticky: true
			});
			
		break;
		case "basic":
			
			var theText 		= /*'<img src="' + image + '" class="img-thumb-themed2" />' +*/ message;
			var themedAnimate = 	function() {
											setTimeout(function() { 
												$('img.img-thumb-themed2').animate({right: "18em"}, 1000);
											}, 10);
										}
					
			$.jGrowl(theText, {
						header: header,
						theme: 'themed2', 
						open: themedAnimate
			});
			
		break;
	}
};
/* @license
morris.js v0.5.0
Copyright 2014 Olly Smith All rights reserved.
Licensed under the BSD-2-Clause License.
*/
(function(){var a,b,c,d,e=[].slice,f=function(a,b){return function(){return a.apply(b,arguments)}},g={}.hasOwnProperty,h=function(a,b){function c(){this.constructor=a}for(var d in b)g.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},i=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};b=window.Morris={},a=jQuery,b.EventEmitter=function(){function a(){}return a.prototype.on=function(a,b){return null==this.handlers&&(this.handlers={}),null==this.handlers[a]&&(this.handlers[a]=[]),this.handlers[a].push(b),this},a.prototype.fire=function(){var a,b,c,d,f,g,h;if(c=arguments[0],a=2<=arguments.length?e.call(arguments,1):[],null!=this.handlers&&null!=this.handlers[c]){for(g=this.handlers[c],h=[],d=0,f=g.length;f>d;d++)b=g[d],h.push(b.apply(null,a));return h}},a}(),b.commas=function(a){var b,c,d,e;return null!=a?(d=0>a?"-":"",b=Math.abs(a),c=Math.floor(b).toFixed(0),d+=c.replace(/(?=(?:\d{3})+$)(?!^)/g,","),e=b.toString(),e.length>c.length&&(d+=e.slice(c.length)),d):"-"},b.pad2=function(a){return(10>a?"0":"")+a},b.Grid=function(c){function d(b){this.resizeHandler=f(this.resizeHandler,this);var c=this;if(this.el="string"==typeof b.element?a(document.getElementById(b.element)):a(b.element),null==this.el||0===this.el.length)throw new Error("Graph container element not found");"static"===this.el.css("position")&&this.el.css("position","relative"),this.options=a.extend({},this.gridDefaults,this.defaults||{},b),"string"==typeof this.options.units&&(this.options.postUnits=b.units),this.raphael=new Raphael(this.el[0]),this.elementWidth=null,this.elementHeight=null,this.dirty=!1,this.selectFrom=null,this.init&&this.init(),this.setData(this.options.data),this.el.bind("mousemove",function(a){var b,d,e,f,g;return d=c.el.offset(),g=a.pageX-d.left,c.selectFrom?(b=c.data[c.hitTest(Math.min(g,c.selectFrom))]._x,e=c.data[c.hitTest(Math.max(g,c.selectFrom))]._x,f=e-b,c.selectionRect.attr({x:b,width:f})):c.fire("hovermove",g,a.pageY-d.top)}),this.el.bind("mouseleave",function(){return c.selectFrom&&(c.selectionRect.hide(),c.selectFrom=null),c.fire("hoverout")}),this.el.bind("touchstart touchmove touchend",function(a){var b,d;return d=a.originalEvent.touches[0]||a.originalEvent.changedTouches[0],b=c.el.offset(),c.fire("hovermove",d.pageX-b.left,d.pageY-b.top)}),this.el.bind("click",function(a){var b;return b=c.el.offset(),c.fire("gridclick",a.pageX-b.left,a.pageY-b.top)}),this.options.rangeSelect&&(this.selectionRect=this.raphael.rect(0,0,0,this.el.innerHeight()).attr({fill:this.options.rangeSelectColor,stroke:!1}).toBack().hide(),this.el.bind("mousedown",function(a){var b;return b=c.el.offset(),c.startRange(a.pageX-b.left)}),this.el.bind("mouseup",function(a){var b;return b=c.el.offset(),c.endRange(a.pageX-b.left),c.fire("hovermove",a.pageX-b.left,a.pageY-b.top)})),this.options.resize&&a(window).bind("resize",function(){return null!=c.timeoutId&&window.clearTimeout(c.timeoutId),c.timeoutId=window.setTimeout(c.resizeHandler,100)}),this.el.css("-webkit-tap-highlight-color","rgba(0,0,0,0)"),this.postInit&&this.postInit()}return h(d,c),d.prototype.gridDefaults={dateFormat:null,axes:!0,grid:!0,gridLineColor:"#aaa",gridStrokeWidth:.5,gridTextColor:"#888",gridTextSize:12,gridTextFamily:"sans-serif",gridTextWeight:"normal",hideHover:!1,yLabelFormat:null,xLabelAngle:0,numLines:5,padding:25,parseTime:!0,postUnits:"",preUnits:"",ymax:"auto",ymin:"auto 0",goals:[],goalStrokeWidth:1,goalLineColors:["#666633","#999966","#cc6666","#663333"],events:[],eventStrokeWidth:1,eventLineColors:["#005a04","#ccffbb","#3a5f0b","#005502"],rangeSelect:null,rangeSelectColor:"#eef",resize:!1},d.prototype.setData=function(a,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;return null==c&&(c=!0),this.options.data=a,null==a||0===a.length?(this.data=[],this.raphael.clear(),null!=this.hover&&this.hover.hide(),void 0):(o=this.cumulative?0:null,p=this.cumulative?0:null,this.options.goals.length>0&&(h=Math.min.apply(Math,this.options.goals),g=Math.max.apply(Math,this.options.goals),p=null!=p?Math.min(p,h):h,o=null!=o?Math.max(o,g):g),this.data=function(){var c,d,g;for(g=[],f=c=0,d=a.length;d>c;f=++c)j=a[f],i={src:j},i.label=j[this.options.xkey],this.options.parseTime?(i.x=b.parseDate(i.label),this.options.dateFormat?i.label=this.options.dateFormat(i.x):"number"==typeof i.label&&(i.label=new Date(i.label).toString())):(i.x=f,this.options.xLabelFormat&&(i.label=this.options.xLabelFormat(i))),l=0,i.y=function(){var a,b,c,d;for(c=this.options.ykeys,d=[],e=a=0,b=c.length;b>a;e=++a)n=c[e],q=j[n],"string"==typeof q&&(q=parseFloat(q)),null!=q&&"number"!=typeof q&&(q=null),null!=q&&(this.cumulative?l+=q:null!=o?(o=Math.max(q,o),p=Math.min(q,p)):o=p=q),this.cumulative&&null!=l&&(o=Math.max(l,o),p=Math.min(l,p)),d.push(q);return d}.call(this),g.push(i);return g}.call(this),this.options.parseTime&&(this.data=this.data.sort(function(a,b){return(a.x>b.x)-(b.x>a.x)})),this.xmin=this.data[0].x,this.xmax=this.data[this.data.length-1].x,this.events=[],this.options.events.length>0&&(this.events=this.options.parseTime?function(){var a,c,e,f;for(e=this.options.events,f=[],a=0,c=e.length;c>a;a++)d=e[a],f.push(b.parseDate(d));return f}.call(this):this.options.events,this.xmax=Math.max(this.xmax,Math.max.apply(Math,this.events)),this.xmin=Math.min(this.xmin,Math.min.apply(Math,this.events))),this.xmin===this.xmax&&(this.xmin-=1,this.xmax+=1),this.ymin=this.yboundary("min",p),this.ymax=this.yboundary("max",o),this.ymin===this.ymax&&(p&&(this.ymin-=1),this.ymax+=1),((r=this.options.axes)===!0||"both"===r||"y"===r||this.options.grid===!0)&&(this.options.ymax===this.gridDefaults.ymax&&this.options.ymin===this.gridDefaults.ymin?(this.grid=this.autoGridLines(this.ymin,this.ymax,this.options.numLines),this.ymin=Math.min(this.ymin,this.grid[0]),this.ymax=Math.max(this.ymax,this.grid[this.grid.length-1])):(k=(this.ymax-this.ymin)/(this.options.numLines-1),this.grid=function(){var a,b,c,d;for(d=[],m=a=b=this.ymin,c=this.ymax;k>0?c>=a:a>=c;m=a+=k)d.push(m);return d}.call(this))),this.dirty=!0,c?this.redraw():void 0)},d.prototype.yboundary=function(a,b){var c,d;return c=this.options["y"+a],"string"==typeof c?"auto"===c.slice(0,4)?c.length>5?(d=parseInt(c.slice(5),10),null==b?d:Math[a](b,d)):null!=b?b:0:parseInt(c,10):c},d.prototype.autoGridLines=function(a,b,c){var d,e,f,g,h,i,j,k,l;return h=b-a,l=Math.floor(Math.log(h)/Math.log(10)),j=Math.pow(10,l),e=Math.floor(a/j)*j,d=Math.ceil(b/j)*j,i=(d-e)/(c-1),1===j&&i>1&&Math.ceil(i)!==i&&(i=Math.ceil(i),d=e+i*(c-1)),0>e&&d>0&&(e=Math.floor(a/i)*i,d=Math.ceil(b/i)*i),1>i?(g=Math.floor(Math.log(i)/Math.log(10)),f=function(){var a,b;for(b=[],k=a=e;i>0?d>=a:a>=d;k=a+=i)b.push(parseFloat(k.toFixed(1-g)));return b}()):f=function(){var a,b;for(b=[],k=a=e;i>0?d>=a:a>=d;k=a+=i)b.push(k);return b}(),f},d.prototype._calc=function(){var a,b,c,d,e,f,g,h;return e=this.el.width(),c=this.el.height(),(this.elementWidth!==e||this.elementHeight!==c||this.dirty)&&(this.elementWidth=e,this.elementHeight=c,this.dirty=!1,this.left=this.options.padding,this.right=this.elementWidth-this.options.padding,this.top=this.options.padding,this.bottom=this.elementHeight-this.options.padding,((g=this.options.axes)===!0||"both"===g||"y"===g)&&(f=function(){var a,c,d,e;for(d=this.grid,e=[],a=0,c=d.length;c>a;a++)b=d[a],e.push(this.measureText(this.yAxisFormat(b)).width);return e}.call(this),this.left+=Math.max.apply(Math,f)),((h=this.options.axes)===!0||"both"===h||"x"===h)&&(a=function(){var a,b,c;for(c=[],d=a=0,b=this.data.length;b>=0?b>a:a>b;d=b>=0?++a:--a)c.push(this.measureText(this.data[d].text,-this.options.xLabelAngle).height);return c}.call(this),this.bottom-=Math.max.apply(Math,a)),this.width=Math.max(1,this.right-this.left),this.height=Math.max(1,this.bottom-this.top),this.dx=this.width/(this.xmax-this.xmin),this.dy=this.height/(this.ymax-this.ymin),this.calc)?this.calc():void 0},d.prototype.transY=function(a){return this.bottom-(a-this.ymin)*this.dy},d.prototype.transX=function(a){return 1===this.data.length?(this.left+this.right)/2:this.left+(a-this.xmin)*this.dx},d.prototype.redraw=function(){return this.raphael.clear(),this._calc(),this.drawGrid(),this.drawGoals(),this.drawEvents(),this.draw?this.draw():void 0},d.prototype.measureText=function(a,b){var c,d;return null==b&&(b=0),d=this.raphael.text(100,100,a).attr("font-size",this.options.gridTextSize).attr("font-family",this.options.gridTextFamily).attr("font-weight",this.options.gridTextWeight).rotate(b),c=d.getBBox(),d.remove(),c},d.prototype.yAxisFormat=function(a){return this.yLabelFormat(a)},d.prototype.yLabelFormat=function(a){return"function"==typeof this.options.yLabelFormat?this.options.yLabelFormat(a):""+this.options.preUnits+b.commas(a)+this.options.postUnits},d.prototype.drawGrid=function(){var a,b,c,d,e,f,g,h;if(this.options.grid!==!1||(e=this.options.axes)===!0||"both"===e||"y"===e){for(f=this.grid,h=[],c=0,d=f.length;d>c;c++)a=f[c],b=this.transY(a),((g=this.options.axes)===!0||"both"===g||"y"===g)&&this.drawYAxisLabel(this.left-this.options.padding/2,b,this.yAxisFormat(a)),this.options.grid?h.push(this.drawGridLine("M"+this.left+","+b+"H"+(this.left+this.width))):h.push(void 0);return h}},d.prototype.drawGoals=function(){var a,b,c,d,e,f,g;for(f=this.options.goals,g=[],c=d=0,e=f.length;e>d;c=++d)b=f[c],a=this.options.goalLineColors[c%this.options.goalLineColors.length],g.push(this.drawGoal(b,a));return g},d.prototype.drawEvents=function(){var a,b,c,d,e,f,g;for(f=this.events,g=[],c=d=0,e=f.length;e>d;c=++d)b=f[c],a=this.options.eventLineColors[c%this.options.eventLineColors.length],g.push(this.drawEvent(b,a));return g},d.prototype.drawGoal=function(a,b){return this.raphael.path("M"+this.left+","+this.transY(a)+"H"+this.right).attr("stroke",b).attr("stroke-width",this.options.goalStrokeWidth)},d.prototype.drawEvent=function(a,b){return this.raphael.path("M"+this.transX(a)+","+this.bottom+"V"+this.top).attr("stroke",b).attr("stroke-width",this.options.eventStrokeWidth)},d.prototype.drawYAxisLabel=function(a,b,c){return this.raphael.text(a,b,c).attr("font-size",this.options.gridTextSize).attr("font-family",this.options.gridTextFamily).attr("font-weight",this.options.gridTextWeight).attr("fill",this.options.gridTextColor).attr("text-anchor","end")},d.prototype.drawGridLine=function(a){return this.raphael.path(a).attr("stroke",this.options.gridLineColor).attr("stroke-width",this.options.gridStrokeWidth)},d.prototype.startRange=function(a){return this.hover.hide(),this.selectFrom=a,this.selectionRect.attr({x:a,width:0}).show()},d.prototype.endRange=function(a){var b,c;return this.selectFrom?(c=Math.min(this.selectFrom,a),b=Math.max(this.selectFrom,a),this.options.rangeSelect.call(this.el,{start:this.data[this.hitTest(c)].x,end:this.data[this.hitTest(b)].x}),this.selectFrom=null):void 0},d.prototype.resizeHandler=function(){return this.timeoutId=null,this.raphael.setSize(this.el.width(),this.el.height()),this.redraw()},d}(b.EventEmitter),b.parseDate=function(a){var b,c,d,e,f,g,h,i,j,k,l;return"number"==typeof a?a:(c=a.match(/^(\d+) Q(\d)$/),e=a.match(/^(\d+)-(\d+)$/),f=a.match(/^(\d+)-(\d+)-(\d+)$/),h=a.match(/^(\d+) W(\d+)$/),i=a.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+)(Z|([+-])(\d\d):?(\d\d))?$/),j=a.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+):(\d+(\.\d+)?)(Z|([+-])(\d\d):?(\d\d))?$/),c?new Date(parseInt(c[1],10),3*parseInt(c[2],10)-1,1).getTime():e?new Date(parseInt(e[1],10),parseInt(e[2],10)-1,1).getTime():f?new Date(parseInt(f[1],10),parseInt(f[2],10)-1,parseInt(f[3],10)).getTime():h?(k=new Date(parseInt(h[1],10),0,1),4!==k.getDay()&&k.setMonth(0,1+(4-k.getDay()+7)%7),k.getTime()+6048e5*parseInt(h[2],10)):i?i[6]?(g=0,"Z"!==i[6]&&(g=60*parseInt(i[8],10)+parseInt(i[9],10),"+"===i[7]&&(g=0-g)),Date.UTC(parseInt(i[1],10),parseInt(i[2],10)-1,parseInt(i[3],10),parseInt(i[4],10),parseInt(i[5],10)+g)):new Date(parseInt(i[1],10),parseInt(i[2],10)-1,parseInt(i[3],10),parseInt(i[4],10),parseInt(i[5],10)).getTime():j?(l=parseFloat(j[6]),b=Math.floor(l),d=Math.round(1e3*(l-b)),j[8]?(g=0,"Z"!==j[8]&&(g=60*parseInt(j[10],10)+parseInt(j[11],10),"+"===j[9]&&(g=0-g)),Date.UTC(parseInt(j[1],10),parseInt(j[2],10)-1,parseInt(j[3],10),parseInt(j[4],10),parseInt(j[5],10)+g,b,d)):new Date(parseInt(j[1],10),parseInt(j[2],10)-1,parseInt(j[3],10),parseInt(j[4],10),parseInt(j[5],10),b,d).getTime()):new Date(parseInt(a,10),0,1).getTime())},b.Hover=function(){function c(c){null==c&&(c={}),this.options=a.extend({},b.Hover.defaults,c),this.el=a("<div class='"+this.options["class"]+"'></div>"),this.el.hide(),this.options.parent.append(this.el)}return c.defaults={"class":"morris-hover morris-default-style"},c.prototype.update=function(a,b,c){return a?(this.html(a),this.show(),this.moveTo(b,c)):this.hide()},c.prototype.html=function(a){return this.el.html(a)},c.prototype.moveTo=function(a,b){var c,d,e,f,g,h;return g=this.options.parent.innerWidth(),f=this.options.parent.innerHeight(),d=this.el.outerWidth(),c=this.el.outerHeight(),e=Math.min(Math.max(0,a-d/2),g-d),null!=b?(h=b-c-10,0>h&&(h=b+10,h+c>f&&(h=f/2-c/2))):h=f/2-c/2,this.el.css({left:e+"px",top:parseInt(h)+"px"})},c.prototype.show=function(){return this.el.show()},c.prototype.hide=function(){return this.el.hide()},c}(),b.Line=function(a){function c(a){return this.hilight=f(this.hilight,this),this.onHoverOut=f(this.onHoverOut,this),this.onHoverMove=f(this.onHoverMove,this),this.onGridClick=f(this.onGridClick,this),this instanceof b.Line?(c.__super__.constructor.call(this,a),void 0):new b.Line(a)}return h(c,a),c.prototype.init=function(){return"always"!==this.options.hideHover?(this.hover=new b.Hover({parent:this.el}),this.on("hovermove",this.onHoverMove),this.on("hoverout",this.onHoverOut),this.on("gridclick",this.onGridClick)):void 0},c.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],pointStrokeWidths:[1],pointStrokeColors:["#ffffff"],pointFillColors:[],smooth:!0,xLabels:"auto",xLabelFormat:null,xLabelMargin:24,hideHover:!1},c.prototype.calc=function(){return this.calcPoints(),this.generatePaths()},c.prototype.calcPoints=function(){var a,b,c,d,e,f;for(e=this.data,f=[],c=0,d=e.length;d>c;c++)a=e[c],a._x=this.transX(a.x),a._y=function(){var c,d,e,f;for(e=a.y,f=[],c=0,d=e.length;d>c;c++)b=e[c],null!=b?f.push(this.transY(b)):f.push(b);return f}.call(this),f.push(a._ymax=Math.min.apply(Math,[this.bottom].concat(function(){var c,d,e,f;for(e=a._y,f=[],c=0,d=e.length;d>c;c++)b=e[c],null!=b&&f.push(b);return f}())));return f},c.prototype.hitTest=function(a){var b,c,d,e,f;if(0===this.data.length)return null;for(f=this.data.slice(1),b=d=0,e=f.length;e>d&&(c=f[b],!(a<(c._x+this.data[b]._x)/2));b=++d);return b},c.prototype.onGridClick=function(a,b){var c;return c=this.hitTest(a),this.fire("click",c,this.data[c].src,a,b)},c.prototype.onHoverMove=function(a){var b;return b=this.hitTest(a),this.displayHoverForRow(b)},c.prototype.onHoverOut=function(){return this.options.hideHover!==!1?this.displayHoverForRow(null):void 0},c.prototype.displayHoverForRow=function(a){var b;return null!=a?((b=this.hover).update.apply(b,this.hoverContentForRow(a)),this.hilight(a)):(this.hover.hide(),this.hilight())},c.prototype.hoverContentForRow=function(a){var b,c,d,e,f,g,h;for(d=this.data[a],b="<div class='morris-hover-row-label'>"+d.label+"</div>",h=d.y,c=f=0,g=h.length;g>f;c=++f)e=h[c],b+="<div class='morris-hover-point' style='color: "+this.colorFor(d,c,"label")+"'>\n  "+this.options.labels[c]+":\n  "+this.yLabelFormat(e)+"\n</div>";return"function"==typeof this.options.hoverCallback&&(b=this.options.hoverCallback(a,this.options,b,d.src)),[b,d._x,d._ymax]},c.prototype.generatePaths=function(){var a,c,d,e;return this.paths=function(){var f,g,h,j;for(j=[],c=f=0,g=this.options.ykeys.length;g>=0?g>f:f>g;c=g>=0?++f:--f)e="boolean"==typeof this.options.smooth?this.options.smooth:(h=this.options.ykeys[c],i.call(this.options.smooth,h)>=0),a=function(){var a,b,e,f;for(e=this.data,f=[],a=0,b=e.length;b>a;a++)d=e[a],void 0!==d._y[c]&&f.push({x:d._x,y:d._y[c]});return f}.call(this),a.length>1?j.push(b.Line.createPath(a,e,this.bottom)):j.push(null);return j}.call(this)},c.prototype.draw=function(){var a;return((a=this.options.axes)===!0||"both"===a||"x"===a)&&this.drawXAxis(),this.drawSeries(),this.options.hideHover===!1?this.displayHoverForRow(this.data.length-1):void 0},c.prototype.drawXAxis=function(){var a,c,d,e,f,g,h,i,j,k,l=this;for(h=this.bottom+this.options.padding/2,f=null,e=null,a=function(a,b){var c,d,g,i,j;return c=l.drawXAxisLabel(l.transX(b),h,a),j=c.getBBox(),c.transform("r"+-l.options.xLabelAngle),d=c.getBBox(),c.transform("t0,"+d.height/2+"..."),0!==l.options.xLabelAngle&&(i=-.5*j.width*Math.cos(l.options.xLabelAngle*Math.PI/180),c.transform("t"+i+",0...")),d=c.getBBox(),(null==f||f>=d.x+d.width||null!=e&&e>=d.x)&&d.x>=0&&d.x+d.width<l.el.width()?(0!==l.options.xLabelAngle&&(g=1.25*l.options.gridTextSize/Math.sin(l.options.xLabelAngle*Math.PI/180),e=d.x-g),f=d.x-l.options.xLabelMargin):c.remove()},d=this.options.parseTime?1===this.data.length&&"auto"===this.options.xLabels?[[this.data[0].label,this.data[0].x]]:b.labelSeries(this.xmin,this.xmax,this.width,this.options.xLabels,this.options.xLabelFormat):function(){var a,b,c,d;for(c=this.data,d=[],a=0,b=c.length;b>a;a++)g=c[a],d.push([g.label,g.x]);return d}.call(this),d.reverse(),k=[],i=0,j=d.length;j>i;i++)c=d[i],k.push(a(c[0],c[1]));return k},c.prototype.drawSeries=function(){var a,b,c,d,e,f;for(this.seriesPoints=[],a=b=d=this.options.ykeys.length-1;0>=d?0>=b:b>=0;a=0>=d?++b:--b)this._drawLineFor(a);for(f=[],a=c=e=this.options.ykeys.length-1;0>=e?0>=c:c>=0;a=0>=e?++c:--c)f.push(this._drawPointFor(a));return f},c.prototype._drawPointFor=function(a){var b,c,d,e,f,g;for(this.seriesPoints[a]=[],f=this.data,g=[],d=0,e=f.length;e>d;d++)c=f[d],b=null,null!=c._y[a]&&(b=this.drawLinePoint(c._x,c._y[a],this.colorFor(c,a,"point"),a)),g.push(this.seriesPoints[a].push(b));return g},c.prototype._drawLineFor=function(a){var b;return b=this.paths[a],null!==b?this.drawLinePath(b,this.colorFor(null,a,"line"),a):void 0},c.createPath=function(a,c,d){var e,f,g,h,i,j,k,l,m,n,o,p,q,r;for(k="",c&&(g=b.Line.gradients(a)),l={y:null},h=q=0,r=a.length;r>q;h=++q)e=a[h],null!=e.y&&(null!=l.y?c?(f=g[h],j=g[h-1],i=(e.x-l.x)/4,m=l.x+i,o=Math.min(d,l.y+i*j),n=e.x-i,p=Math.min(d,e.y-i*f),k+="C"+m+","+o+","+n+","+p+","+e.x+","+e.y):k+="L"+e.x+","+e.y:c&&null==g[h]||(k+="M"+e.x+","+e.y)),l=e;return k},c.gradients=function(a){var b,c,d,e,f,g,h,i;for(c=function(a,b){return(a.y-b.y)/(a.x-b.x)},i=[],d=g=0,h=a.length;h>g;d=++g)b=a[d],null!=b.y?(e=a[d+1]||{y:null},f=a[d-1]||{y:null},null!=f.y&&null!=e.y?i.push(c(f,e)):null!=f.y?i.push(c(f,b)):null!=e.y?i.push(c(b,e)):i.push(null)):i.push(null);return i},c.prototype.hilight=function(a){var b,c,d,e,f;if(null!==this.prevHilight&&this.prevHilight!==a)for(b=c=0,e=this.seriesPoints.length-1;e>=0?e>=c:c>=e;b=e>=0?++c:--c)this.seriesPoints[b][this.prevHilight]&&this.seriesPoints[b][this.prevHilight].animate(this.pointShrinkSeries(b));if(null!==a&&this.prevHilight!==a)for(b=d=0,f=this.seriesPoints.length-1;f>=0?f>=d:d>=f;b=f>=0?++d:--d)this.seriesPoints[b][a]&&this.seriesPoints[b][a].animate(this.pointGrowSeries(b));return this.prevHilight=a},c.prototype.colorFor=function(a,b,c){return"function"==typeof this.options.lineColors?this.options.lineColors.call(this,a,b,c):"point"===c?this.options.pointFillColors[b%this.options.pointFillColors.length]||this.options.lineColors[b%this.options.lineColors.length]:this.options.lineColors[b%this.options.lineColors.length]},c.prototype.drawXAxisLabel=function(a,b,c){return this.raphael.text(a,b,c).attr("font-size",this.options.gridTextSize).attr("font-family",this.options.gridTextFamily).attr("font-weight",this.options.gridTextWeight).attr("fill",this.options.gridTextColor)},c.prototype.drawLinePath=function(a,b,c){return this.raphael.path(a).attr("stroke",b).attr("stroke-width",this.lineWidthForSeries(c))},c.prototype.drawLinePoint=function(a,b,c,d){return this.raphael.circle(a,b,this.pointSizeForSeries(d)).attr("fill",c).attr("stroke-width",this.pointStrokeWidthForSeries(d)).attr("stroke",this.pointStrokeColorForSeries(d))},c.prototype.pointStrokeWidthForSeries=function(a){return this.options.pointStrokeWidths[a%this.options.pointStrokeWidths.length]},c.prototype.pointStrokeColorForSeries=function(a){return this.options.pointStrokeColors[a%this.options.pointStrokeColors.length]},c.prototype.lineWidthForSeries=function(a){return this.options.lineWidth instanceof Array?this.options.lineWidth[a%this.options.lineWidth.length]:this.options.lineWidth},c.prototype.pointSizeForSeries=function(a){return this.options.pointSize instanceof Array?this.options.pointSize[a%this.options.pointSize.length]:this.options.pointSize},c.prototype.pointGrowSeries=function(a){return Raphael.animation({r:this.pointSizeForSeries(a)+3},25,"linear")},c.prototype.pointShrinkSeries=function(a){return Raphael.animation({r:this.pointSizeForSeries(a)},25,"linear")},c}(b.Grid),b.labelSeries=function(c,d,e,f,g){var h,i,j,k,l,m,n,o,p,q,r;if(j=200*(d-c)/e,i=new Date(c),n=b.LABEL_SPECS[f],void 0===n)for(r=b.AUTO_LABEL_ORDER,p=0,q=r.length;q>p;p++)if(k=r[p],m=b.LABEL_SPECS[k],j>=m.span){n=m;break}for(void 0===n&&(n=b.LABEL_SPECS.second),g&&(n=a.extend({},n,{fmt:g})),h=n.start(i),l=[];(o=h.getTime())<=d;)o>=c&&l.push([n.fmt(h),o]),n.incr(h);return l},c=function(a){return{span:60*a*1e3,start:function(a){return new Date(a.getFullYear(),a.getMonth(),a.getDate(),a.getHours())},fmt:function(a){return""+b.pad2(a.getHours())+":"+b.pad2(a.getMinutes())},incr:function(b){return b.setUTCMinutes(b.getUTCMinutes()+a)}}},d=function(a){return{span:1e3*a,start:function(a){return new Date(a.getFullYear(),a.getMonth(),a.getDate(),a.getHours(),a.getMinutes())},fmt:function(a){return""+b.pad2(a.getHours())+":"+b.pad2(a.getMinutes())+":"+b.pad2(a.getSeconds())},incr:function(b){return b.setUTCSeconds(b.getUTCSeconds()+a)}}},b.LABEL_SPECS={decade:{span:1728e8,start:function(a){return new Date(a.getFullYear()-a.getFullYear()%10,0,1)},fmt:function(a){return""+a.getFullYear()},incr:function(a){return a.setFullYear(a.getFullYear()+10)}},year:{span:1728e7,start:function(a){return new Date(a.getFullYear(),0,1)},fmt:function(a){return""+a.getFullYear()},incr:function(a){return a.setFullYear(a.getFullYear()+1)}},month:{span:24192e5,start:function(a){return new Date(a.getFullYear(),a.getMonth(),1)},fmt:function(a){return""+a.getFullYear()+"-"+b.pad2(a.getMonth()+1)},incr:function(a){return a.setMonth(a.getMonth()+1)}},week:{span:6048e5,start:function(a){return new Date(a.getFullYear(),a.getMonth(),a.getDate())},fmt:function(a){return""+a.getFullYear()+"-"+b.pad2(a.getMonth()+1)+"-"+b.pad2(a.getDate())},incr:function(a){return a.setDate(a.getDate()+7)}},day:{span:864e5,start:function(a){return new Date(a.getFullYear(),a.getMonth(),a.getDate())},fmt:function(a){return""+a.getFullYear()+"-"+b.pad2(a.getMonth()+1)+"-"+b.pad2(a.getDate())},incr:function(a){return a.setDate(a.getDate()+1)}},hour:c(60),"30min":c(30),"15min":c(15),"10min":c(10),"5min":c(5),minute:c(1),"30sec":d(30),"15sec":d(15),"10sec":d(10),"5sec":d(5),second:d(1)},b.AUTO_LABEL_ORDER=["decade","year","month","week","day","hour","30min","15min","10min","5min","minute","30sec","15sec","10sec","5sec","second"],b.Area=function(c){function d(c){var f;return this instanceof b.Area?(f=a.extend({},e,c),this.cumulative=!f.behaveLikeLine,"auto"===f.fillOpacity&&(f.fillOpacity=f.behaveLikeLine?.8:1),d.__super__.constructor.call(this,f),void 0):new b.Area(c)}var e;return h(d,c),e={fillOpacity:"auto",behaveLikeLine:!1},d.prototype.calcPoints=function(){var a,b,c,d,e,f,g;for(f=this.data,g=[],d=0,e=f.length;e>d;d++)a=f[d],a._x=this.transX(a.x),b=0,a._y=function(){var d,e,f,g;for(f=a.y,g=[],d=0,e=f.length;e>d;d++)c=f[d],this.options.behaveLikeLine?g.push(this.transY(c)):(b+=c||0,g.push(this.transY(b)));return g}.call(this),g.push(a._ymax=Math.max.apply(Math,a._y));return g},d.prototype.drawSeries=function(){var a,b,c,d,e,f,g,h;for(this.seriesPoints=[],b=this.options.behaveLikeLine?function(){f=[];for(var a=0,b=this.options.ykeys.length-1;b>=0?b>=a:a>=b;b>=0?a++:a--)f.push(a);return f}.apply(this):function(){g=[];for(var a=e=this.options.ykeys.length-1;0>=e?0>=a:a>=0;0>=e?a++:a--)g.push(a);return g}.apply(this),h=[],c=0,d=b.length;d>c;c++)a=b[c],this._drawFillFor(a),this._drawLineFor(a),h.push(this._drawPointFor(a));return h},d.prototype._drawFillFor=function(a){var b;return b=this.paths[a],null!==b?(b+="L"+this.transX(this.xmax)+","+this.bottom+"L"+this.transX(this.xmin)+","+this.bottom+"Z",this.drawFilledPath(b,this.fillForSeries(a))):void 0},d.prototype.fillForSeries=function(a){var b;return b=Raphael.rgb2hsl(this.colorFor(this.data[a],a,"line")),Raphael.hsl(b.h,this.options.behaveLikeLine?.9*b.s:.75*b.s,Math.min(.98,this.options.behaveLikeLine?1.2*b.l:1.25*b.l))},d.prototype.drawFilledPath=function(a,b){return this.raphael.path(a).attr("fill",b).attr("fill-opacity",this.options.fillOpacity).attr("stroke","none")},d}(b.Line),b.Bar=function(c){function d(c){return this.onHoverOut=f(this.onHoverOut,this),this.onHoverMove=f(this.onHoverMove,this),this.onGridClick=f(this.onGridClick,this),this instanceof b.Bar?(d.__super__.constructor.call(this,a.extend({},c,{parseTime:!1})),void 0):new b.Bar(c)}return h(d,c),d.prototype.init=function(){return this.cumulative=this.options.stacked,"always"!==this.options.hideHover?(this.hover=new b.Hover({parent:this.el}),this.on("hovermove",this.onHoverMove),this.on("hoverout",this.onHoverOut),this.on("gridclick",this.onGridClick)):void 0},d.prototype.defaults={barSizeRatio:.75,barGap:3,barColors:["#0b62a4","#7a92a3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],barOpacity:1,barRadius:[0,0,0,0],xLabelMargin:50},d.prototype.calc=function(){var a;return this.calcBars(),this.options.hideHover===!1?(a=this.hover).update.apply(a,this.hoverContentForRow(this.data.length-1)):void 0},d.prototype.calcBars=function(){var a,b,c,d,e,f,g;for(f=this.data,g=[],a=d=0,e=f.length;e>d;a=++d)b=f[a],b._x=this.left+this.width*(a+.5)/this.data.length,g.push(b._y=function(){var a,d,e,f;for(e=b.y,f=[],a=0,d=e.length;d>a;a++)c=e[a],null!=c?f.push(this.transY(c)):f.push(null);return f}.call(this));return g},d.prototype.draw=function(){var a;return((a=this.options.axes)===!0||"both"===a||"x"===a)&&this.drawXAxis(),this.drawSeries()},d.prototype.drawXAxis=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m;for(j=this.bottom+(this.options.xAxisLabelTopPadding||this.options.padding/2),g=null,f=null,m=[],a=k=0,l=this.data.length;l>=0?l>k:k>l;a=l>=0?++k:--k)h=this.data[this.data.length-1-a],b=this.drawXAxisLabel(h._x,j,h.label),i=b.getBBox(),b.transform("r"+-this.options.xLabelAngle),c=b.getBBox(),b.transform("t0,"+c.height/2+"..."),0!==this.options.xLabelAngle&&(e=-.5*i.width*Math.cos(this.options.xLabelAngle*Math.PI/180),b.transform("t"+e+",0...")),(null==g||g>=c.x+c.width||null!=f&&f>=c.x)&&c.x>=0&&c.x+c.width<this.el.width()?(0!==this.options.xLabelAngle&&(d=1.25*this.options.gridTextSize/Math.sin(this.options.xLabelAngle*Math.PI/180),f=c.x-d),m.push(g=c.x-this.options.xLabelMargin)):m.push(b.remove());return m},d.prototype.drawSeries=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;return c=this.width/this.options.data.length,h=this.options.stacked?1:this.options.ykeys.length,a=(c*this.options.barSizeRatio-this.options.barGap*(h-1))/h,this.options.barSize&&(a=Math.min(a,this.options.barSize)),l=c-a*h-this.options.barGap*(h-1),g=l/2,o=this.ymin<=0&&this.ymax>=0?this.transY(0):null,this.bars=function(){var h,l,p,q;for(p=this.data,q=[],d=h=0,l=p.length;l>h;d=++h)i=p[d],e=0,q.push(function(){var h,l,p,q;for(p=i._y,q=[],j=h=0,l=p.length;l>h;j=++h)n=p[j],null!==n?(o?(m=Math.min(n,o),b=Math.max(n,o)):(m=n,b=this.bottom),f=this.left+d*c+g,this.options.stacked||(f+=j*(a+this.options.barGap)),k=b-m,this.options.verticalGridCondition&&this.options.verticalGridCondition(i.x)&&this.drawBar(this.left+d*c,this.top,c,Math.abs(this.top-this.bottom),this.options.verticalGridColor,this.options.verticalGridOpacity,this.options.barRadius),this.options.stacked&&(m-=e),this.drawBar(f,m,a,k,this.colorFor(i,j,"bar"),this.options.barOpacity,this.options.barRadius),q.push(e+=k)):q.push(null);return q}.call(this));return q}.call(this)},d.prototype.colorFor=function(a,b,c){var d,e;return"function"==typeof this.options.barColors?(d={x:a.x,y:a.y[b],label:a.label},e={index:b,key:this.options.ykeys[b],label:this.options.labels[b]},this.options.barColors.call(this,d,e,c)):this.options.barColors[b%this.options.barColors.length]},d.prototype.hitTest=function(a){return 0===this.data.length?null:(a=Math.max(Math.min(a,this.right),this.left),Math.min(this.data.length-1,Math.floor((a-this.left)/(this.width/this.data.length))))},d.prototype.onGridClick=function(a,b){var c;return c=this.hitTest(a),this.fire("click",c,this.data[c].src,a,b)},d.prototype.onHoverMove=function(a){var b,c;return b=this.hitTest(a),(c=this.hover).update.apply(c,this.hoverContentForRow(b))},d.prototype.onHoverOut=function(){return this.options.hideHover!==!1?this.hover.hide():void 0},d.prototype.hoverContentForRow=function(a){var b,c,d,e,f,g,h,i;for(d=this.data[a],b="<div class='morris-hover-row-label'>"+d.label+"</div>",i=d.y,c=g=0,h=i.length;h>g;c=++g)f=i[c],b+="<div class='morris-hover-point' style='color: "+this.colorFor(d,c,"label")+"'>\n  "+this.options.labels[c]+":\n  "+this.yLabelFormat(f)+"\n</div>";return"function"==typeof this.options.hoverCallback&&(b=this.options.hoverCallback(a,this.options,b,d.src)),e=this.left+(a+.5)*this.width/this.data.length,[b,e]},d.prototype.drawXAxisLabel=function(a,b,c){var d;return d=this.raphael.text(a,b,c).attr("font-size",this.options.gridTextSize).attr("font-family",this.options.gridTextFamily).attr("font-weight",this.options.gridTextWeight).attr("fill",this.options.gridTextColor)},d.prototype.drawBar=function(a,b,c,d,e,f,g){var h,i;return h=Math.max.apply(Math,g),i=0===h||h>d?this.raphael.rect(a,b,c,d):this.raphael.path(this.roundedRect(a,b,c,d,g)),i.attr("fill",e).attr("fill-opacity",f).attr("stroke","none")},d.prototype.roundedRect=function(a,b,c,d,e){return null==e&&(e=[0,0,0,0]),["M",a,e[0]+b,"Q",a,b,a+e[0],b,"L",a+c-e[1],b,"Q",a+c,b,a+c,b+e[1],"L",a+c,b+d-e[2],"Q",a+c,b+d,a+c-e[2],b+d,"L",a+e[3],b+d,"Q",a,b+d,a,b+d-e[3],"Z"]},d}(b.Grid),b.Donut=function(c){function d(c){this.resizeHandler=f(this.resizeHandler,this),this.select=f(this.select,this),this.click=f(this.click,this);var d=this;if(!(this instanceof b.Donut))return new b.Donut(c);if(this.options=a.extend({},this.defaults,c),this.el="string"==typeof c.element?a(document.getElementById(c.element)):a(c.element),null===this.el||0===this.el.length)throw new Error("Graph placeholder not found.");void 0!==c.data&&0!==c.data.length&&(this.raphael=new Raphael(this.el[0]),this.options.resize&&a(window).bind("resize",function(){return null!=d.timeoutId&&window.clearTimeout(d.timeoutId),d.timeoutId=window.setTimeout(d.resizeHandler,100)}),this.setData(c.data))}return h(d,c),d.prototype.defaults={colors:["#0B62A4","#3980B5","#679DC6","#95BBD7","#B0CCE1","#095791","#095085","#083E67","#052C48","#042135"],backgroundColor:"#FFFFFF",labelColor:"#000000",formatter:b.commas,resize:!1},d.prototype.redraw=function(){var a,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x;for(this.raphael.clear(),c=this.el.width()/2,d=this.el.height()/2,n=(Math.min(c,d)-10)/3,l=0,u=this.values,o=0,r=u.length;r>o;o++)m=u[o],l+=m;for(i=5/(2*n),a=1.9999*Math.PI-i*this.data.length,g=0,f=0,this.segments=[],v=this.values,e=p=0,s=v.length;s>p;e=++p)m=v[e],j=g+i+a*(m/l),k=new b.DonutSegment(c,d,2*n,n,g,j,this.data[e].color||this.options.colors[f%this.options.colors.length],this.options.backgroundColor,f,this.raphael),k.render(),this.segments.push(k),k.on("hover",this.select),k.on("click",this.click),g=j,f+=1;for(this.text1=this.drawEmptyDonutLabel(c,d-10,this.options.labelColor,15,800),this.text2=this.drawEmptyDonutLabel(c,d+10,this.options.labelColor,14),h=Math.max.apply(Math,this.values),f=0,w=this.values,x=[],q=0,t=w.length;t>q;q++){if(m=w[q],m===h){this.select(f);
break}x.push(f+=1)}return x},d.prototype.setData=function(a){var b;return this.data=a,this.values=function(){var a,c,d,e;for(d=this.data,e=[],a=0,c=d.length;c>a;a++)b=d[a],e.push(parseFloat(b.value));return e}.call(this),this.redraw()},d.prototype.click=function(a){return this.fire("click",a,this.data[a])},d.prototype.select=function(a){var b,c,d,e,f,g;for(g=this.segments,e=0,f=g.length;f>e;e++)c=g[e],c.deselect();return d=this.segments[a],d.select(),b=this.data[a],this.setLabels(b.label,this.options.formatter(b.value,b))},d.prototype.setLabels=function(a,b){var c,d,e,f,g,h,i,j;return c=2*(Math.min(this.el.width()/2,this.el.height()/2)-10)/3,f=1.8*c,e=c/2,d=c/3,this.text1.attr({text:a,transform:""}),g=this.text1.getBBox(),h=Math.min(f/g.width,e/g.height),this.text1.attr({transform:"S"+h+","+h+","+(g.x+g.width/2)+","+(g.y+g.height)}),this.text2.attr({text:b,transform:""}),i=this.text2.getBBox(),j=Math.min(f/i.width,d/i.height),this.text2.attr({transform:"S"+j+","+j+","+(i.x+i.width/2)+","+i.y})},d.prototype.drawEmptyDonutLabel=function(a,b,c,d,e){var f;return f=this.raphael.text(a,b,"").attr("font-size",d).attr("fill",c),null!=e&&f.attr("font-weight",e),f},d.prototype.resizeHandler=function(){return this.timeoutId=null,this.raphael.setSize(this.el.width(),this.el.height()),this.redraw()},d}(b.EventEmitter),b.DonutSegment=function(a){function b(a,b,c,d,e,g,h,i,j,k){this.cx=a,this.cy=b,this.inner=c,this.outer=d,this.color=h,this.backgroundColor=i,this.index=j,this.raphael=k,this.deselect=f(this.deselect,this),this.select=f(this.select,this),this.sin_p0=Math.sin(e),this.cos_p0=Math.cos(e),this.sin_p1=Math.sin(g),this.cos_p1=Math.cos(g),this.is_long=g-e>Math.PI?1:0,this.path=this.calcSegment(this.inner+3,this.inner+this.outer-5),this.selectedPath=this.calcSegment(this.inner+3,this.inner+this.outer),this.hilight=this.calcArc(this.inner)}return h(b,a),b.prototype.calcArcPoints=function(a){return[this.cx+a*this.sin_p0,this.cy+a*this.cos_p0,this.cx+a*this.sin_p1,this.cy+a*this.cos_p1]},b.prototype.calcSegment=function(a,b){var c,d,e,f,g,h,i,j,k,l;return k=this.calcArcPoints(a),c=k[0],e=k[1],d=k[2],f=k[3],l=this.calcArcPoints(b),g=l[0],i=l[1],h=l[2],j=l[3],"M"+c+","+e+("A"+a+","+a+",0,"+this.is_long+",0,"+d+","+f)+("L"+h+","+j)+("A"+b+","+b+",0,"+this.is_long+",1,"+g+","+i)+"Z"},b.prototype.calcArc=function(a){var b,c,d,e,f;return f=this.calcArcPoints(a),b=f[0],d=f[1],c=f[2],e=f[3],"M"+b+","+d+("A"+a+","+a+",0,"+this.is_long+",0,"+c+","+e)},b.prototype.render=function(){var a=this;return this.arc=this.drawDonutArc(this.hilight,this.color),this.seg=this.drawDonutSegment(this.path,this.color,this.backgroundColor,function(){return a.fire("hover",a.index)},function(){return a.fire("click",a.index)})},b.prototype.drawDonutArc=function(a,b){return this.raphael.path(a).attr({stroke:b,"stroke-width":2,opacity:0})},b.prototype.drawDonutSegment=function(a,b,c,d,e){return this.raphael.path(a).attr({fill:b,stroke:c,"stroke-width":3}).hover(d).click(e)},b.prototype.select=function(){return this.selected?void 0:(this.seg.animate({path:this.selectedPath},150,"<>"),this.arc.animate({opacity:1},150,"<>"),this.selected=!0)},b.prototype.deselect=function(){return this.selected?(this.seg.animate({path:this.path},150,"<>"),this.arc.animate({opacity:0},150,"<>"),this.selected=!1):void 0},b}(b.EventEmitter)}).call(this);;
/*
    JS VIDEO INFO PARSER
    for ManiaExchange
    by Ozon
*/

function getVideoData(provider, id, type) {
    var viddata = { Title: "", Creator: "500", Thumb: "", Description: "" };

    switch (provider)
    {
        case 'youtube':

            var apikey = 'AIzaSyBMDqTmHDWlPoYraT4D2SkvLT0ulgq1QMw';

            $.ajax({
                url: 'https://www.googleapis.com/youtube/v3/videos?id=' + id + '&key=' + apikey + '&part=snippet,contentDetails,statistics,status',
                type: 'GET',
                dataType: "jsonp",
                success: function (data) {
                    setTimeout(function () {
                        viddata.Title = data.items[0].snippet.title;
                        viddata.Description = data.items[0].snippet.description;
                        viddata.Creator = data.items[0].snippet.channelTitle;
                        viddata.Thumb = data.items[0].snippet.thumbnails.high.url;
                        $('#VideoTitle').val(viddata.Title);
                        $('#VideoDescription').val(viddata.Description);
                        $('#VideoThumbnail').val(viddata.Thumb);
                        $('#VideoThumb').attr('src', viddata.Thumb);
                        $('#VideoAuthor').val(viddata.Creator);
                        $('#VideoPreview #VideoTitle').html(viddata.Title);
                        $('#VideoPreview span').html(viddata.Creator);
                        $('#VideoPreview').removeClass("hidden");
                        $('#formSubmit').removeClass('disabled');
                    }, 500);
                }
            })

            break;

        case 'twitch':

            var apikey = 'i1kki7k0gf35ylf4un7hh4yktn829f';

            var apiurl = 'https://api.twitch.tv/kraken/videos/';
            if (type === 'clip')
                apiurl = 'https://api.twitch.tv/kraken/clips/';

            $.ajax({
                url: apiurl + id,
                headers: {
                    'Client-ID': '' + apikey + '',
                    'Accept': 'application/vnd.twitchtv.v5+json'
                },
                type: 'GET',
                dataType: 'json',
                success: function (data) {
                    setTimeout(function () {
                        viddata.Title = data.title;
                        if (type === 'clip') {
                            viddata.Creator = data.broadcaster.name;
                            viddata.Description = "";
                            viddata.Thumb = data.thumbnails.medium;
                        }
                        else {
                            viddata.Creator = data.channel.display_name;
                            viddata.Description = data.description;
                            viddata.Thumb = data.thumbnails.large[0].url;
                        }
                        $('#VideoTitle').val(viddata.Title);
                        $('#VideoDescription').val(viddata.Description);
                        $('#VideoThumbnail').val(viddata.Thumb);
                        $('#VideoThumb').attr('src', viddata.Thumb);
                        $('#VideoAuthor').val(viddata.Creator);
                        $('#VideoPreview #VideoTitle').html(viddata.Title);
                        $('#VideoPreview span').html(viddata.Creator);
                        $('#VideoPreview').removeClass("hidden");
                        $('#formSubmit').removeClass('disabled');
                    },500);
                }
            });

            break;

        default:
            $('#VideoTitle').val('');
            $('#VideoAuthor').val('');
            $('#VideoThumb').val('');
    }
};
var JSON;JSON||(JSON={}),(function(){"use strict";function i(n){return n<10?"0"+n:n}function f(n){return o.lastIndex=0,o.test(n)?'"'+n.replace(o,function(n){var t=s[n];return typeof t=="string"?t:"\\u"+("0000"+n.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+n+'"'}function r(i,e){var h,l,c,a,v=n,s,o=e[i];o&&typeof o=="object"&&typeof o.toJSON=="function"&&(o=o.toJSON(i)),typeof t=="function"&&(o=t.call(e,i,o));switch(typeof o){case"string":return f(o);case"number":return isFinite(o)?String(o):"null";case"boolean":case"null":return String(o);case"object":if(!o)return"null";n+=u,s=[];if(Object.prototype.toString.apply(o)==="[object Array]"){for(a=o.length,h=0;h<a;h+=1)s[h]=r(h,o)||"null";return c=s.length===0?"[]":n?"[\n"+n+s.join(",\n"+n)+"\n"+v+"]":"["+s.join(",")+"]",n=v,c}if(t&&typeof t=="object")for(a=t.length,h=0;h<a;h+=1)typeof t[h]=="string"&&(l=t[h],c=r(l,o),c&&s.push(f(l)+(n?": ":":")+c));else for(l in o)Object.prototype.hasOwnProperty.call(o,l)&&(c=r(l,o),c&&s.push(f(l)+(n?": ":":")+c));return c=s.length===0?"{}":n?"{\n"+n+s.join(",\n"+n)+"\n"+v+"}":"{"+s.join(",")+"}",n=v,c}}typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+i(this.getUTCMonth()+1)+"-"+i(this.getUTCDate())+"T"+i(this.getUTCHours())+":"+i(this.getUTCMinutes())+":"+i(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var e=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,n,u,s={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},t;typeof JSON.stringify!="function"&&(JSON.stringify=function(i,f,e){var o;n="",u="";if(typeof e=="number")for(o=0;o<e;o+=1)u+=" ";else typeof e=="string"&&(u=e);t=f;if(f&&typeof f!="function"&&(typeof f!="object"||typeof f.length!="number"))throw new Error("JSON.stringify");return r("",{"":i})}),typeof JSON.parse!="function"&&(JSON.parse=function(n,t){function r(n,i){var f,e,u=n[i];if(u&&typeof u=="object")for(f in u)Object.prototype.hasOwnProperty.call(u,f)&&(e=r(u,f),e!==undefined?u[f]=e:delete u[f]);return t.call(n,i,u)}var i;n=String(n),e.lastIndex=0,e.test(n)&&(n=n.replace(e,function(n){return"\\u"+("0000"+n.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(n.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return i=eval("("+n+")"),typeof t=="function"?r({"":i},""):i;throw new SyntaxError("JSON.parse");})})();
!function(s,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((s=s||self).timeago={})}(this,function(s){"use strict";var a=["second","minute","hour","day","week","month","year"];function n(s,n){if(0===n)return["just now","right now"];var e=a[~~(n/2)];return 1<s&&(e+="s"),[s+" "+e+" ago","in "+s+" "+e]}var t=["秒","分钟","小时","天","周","个月","年"];function e(s,n){if(0===n)return["刚刚","片刻后"];var e=t[~~(n/2)];return[s+" "+e+"前",s+" "+e+"后"]}function u(s,n){i[s]=n}function r(s){return i[s]||i.en_US}var i={},o=[60,60,24,7,365/7/12,12];function m(s){return s instanceof Date?s:!isNaN(s)||/^\d+$/.test(s)?new Date(parseInt(s)):(s=(s||"").trim().replace(/\.\d+/,"").replace(/-/,"/").replace(/-/,"/").replace(/(\d)T(\d)/,"$1 $2").replace(/Z/," UTC").replace(/([+-]\d\d):?(\d\d)/," $1$2"),new Date(s))}function d(s,n){for(var e=s<0?1:0,a=s=Math.abs(s),t=0;s>=o[t]&&t<o.length;t++)s/=o[t];return(0===(t*=2)?9:1)<(s=~~s)&&(t+=1),n(s,t,a)[e].replace("%s",s)}function c(s,n){return(+(n=n?m(n):new Date)-+m(s))/1e3}var l="timeago-id";function h(s){return parseInt(s.getAttribute(l))}var g={},f=function(s){clearTimeout(s),delete g[s]};function p(s,n,e,a){f(h(s));var t=a.relativeDate,u=a.minInterval,r=c(n,t);s.innerText=d(r,e);var i=setTimeout(function(){p(s,n,e,a)},Math.min(1e3*Math.max(function(s){for(var n=1,e=0,a=Math.abs(s);s>=o[e]&&e<o.length;e++)s/=o[e],n*=o[e];return a=(a%=n)?n-a:n,Math.ceil(a)}(r),u||1),2147483647));g[i]=0,function(s,n){s.setAttribute(l,n)}(s,i)}u("en_US",n),u("zh_CN",e);var b=[["ثانية","ثانيتين","%s ثوان","%s ثانية"],["دقيقة","دقيقتين","%s دقائق","%s دقيقة"],["ساعة","ساعتين","%s ساعات","%s ساعة"],["يوم","يومين","%s أيام","%s يوماً"],["أسبوع","أسبوعين","%s أسابيع","%s أسبوعاً"],["شهر","شهرين","%s أشهر","%s شهراً"],["عام","عامين","%s أعوام","%s عاماً"]];function v(s,n,e,a,t){var u=t%10,r=a;return 1===t?r=s:1==u&&20<t?r=n:1<u&&u<5&&(20<t||t<10)&&(r=e),r}var y=v.bind(null,"секунду","%s секунду","%s секунды","%s секунд"),k=v.bind(null,"хвіліну","%s хвіліну","%s хвіліны","%s хвілін"),j=v.bind(null,"гадзіну","%s гадзіну","%s гадзіны","%s гадзін"),z=v.bind(null,"дзень","%s дзень","%s дні","%s дзён"),w=v.bind(null,"тыдзень","%s тыдзень","%s тыдні","%s тыдняў"),_=v.bind(null,"месяц","%s месяц","%s месяцы","%s месяцаў"),q=v.bind(null,"год","%s год","%s гады","%s гадоў");function M(s){var n=["۰","۱","۲","۳","۴","۵","۶","۷","۸","۹"];return s.toString().replace(/\d/g,function(s){return n[s]})}var S=[["w tej chwili","za chwilę"],["%s sekund temu","za %s sekund"],["1 minutę temu","za 1 minutę"],["%s minut temu","za %s minut"],["1 godzinę temu","za 1 godzinę"],["%s godzin temu","za %s godzin"],["1 dzień temu","za 1 dzień"],["%s dni temu","za %s dni"],["1 tydzień temu","za 1 tydzień"],["%s tygodni temu","za %s tygodni"],["1 miesiąc temu","za 1 miesiąc"],["%s miesięcy temu","za %s miesięcy"],["1 rok temu","za 1 rok"],["%s lat temu","za %s lat"],["%s sekundy temu","za %s sekundy"],["%s minuty temu","za %s minuty"],["%s godziny temu","za %s godziny"],["%s dni temu","za %s dni"],["%s tygodnie temu","za %s tygodnie"],["%s miesiące temu","za %s miesiące"],["%s lata temu","za %s lata"]];function T(s,n,e,a,t){var u=t%10,r=a;return 1===t?r=s:1==u&&20<t?r=n:1<u&&u<5&&(20<t||t<10)&&(r=e),r}var N=T.bind(null,"секунду","%s секунду","%s секунды","%s секунд"),x=T.bind(null,"минуту","%s минуту","%s минуты","%s минут"),D=T.bind(null,"час","%s час","%s часа","%s часов"),I=T.bind(null,"день","%s день","%s дня","%s дней"),O=T.bind(null,"неделю","%s неделю","%s недели","%s недель"),W=T.bind(null,"месяц","%s месяц","%s месяца","%s месяцев"),$=T.bind(null,"год","%s год","%s года","%s лет");function J(s,n,e,a,t){var u=t%10,r=t%100;return 1==t?s:1==u&&11!=r?n:2<=u&&u<=4&&!(12<=r&&r<=14)?e:a}var U=J.bind(null,"1 секунд","%s секунд","%s секунде","%s секунди"),A=J.bind(null,"1 минут","%s минут","%s минуте","%s минута"),C=J.bind(null,"сат времена","%s сат","%s сата","%s сати"),E=J.bind(null,"1 дан","%s дан","%s дана","%s дана"),B=J.bind(null,"недељу дана","%s недељу","%s недеље","%s недеља"),P=J.bind(null,"месец дана","%s месец","%s месеца","%s месеци"),R=J.bind(null,"годину дана","%s годину","%s године","%s година");function Z(s,n,e,a,t){var u=t%10,r=a;return 1===t?r=s:1==u&&20<t?r=n:1<u&&u<5&&(20<t||t<10)&&(r=e),r}var F=Z.bind(null,"секунду","%s секунду","%s секунди","%s секунд"),G=Z.bind(null,"хвилину","%s хвилину","%s хвилини","%s хвилин"),H=Z.bind(null,"годину","%s годину","%s години","%s годин"),K=Z.bind(null,"день","%s день","%s дні","%s днів"),L=Z.bind(null,"тиждень","%s тиждень","%s тиждні","%s тижднів"),Q=Z.bind(null,"місяць","%s місяць","%s місяці","%s місяців"),V=Z.bind(null,"рік","%s рік","%s роки","%s років");var X=Object.freeze({__proto__:null,ar:function(s,n){if(0===n)return["منذ لحظات","بعد لحظات"];var e=function(s,n){return n<3?b[s][n-1]:3<=n&&n<=10?b[s][2]:b[s][3]}(Math.floor(n/2),s);return["منذ "+e,"بعد "+e]},be:function(s,n){switch(n){case 0:return["толькі што","праз некалькі секунд"];case 1:return[y(s)+" таму","праз "+y(s)];case 2:case 3:return[k(s)+" таму","праз "+k(s)];case 4:case 5:return[j(s)+" таму","праз "+j(s)];case 6:case 7:return[z(s)+" таму","праз "+z(s)];case 8:case 9:return[w(s)+" таму","праз "+w(s)];case 10:case 11:return[_(s)+" таму","праз "+_(s)];case 12:case 13:return[q(s)+" таму","праз "+q(s)];default:return["",""]}},bg:function(s,n){return[["току що","съвсем скоро"],["преди %s секунди","след %s секунди"],["преди 1 минута","след 1 минута"],["преди %s минути","след %s минути"],["преди 1 час","след 1 час"],["преди %s часа","след %s часа"],["преди 1 ден","след 1 ден"],["преди %s дни","след %s дни"],["преди 1 седмица","след 1 седмица"],["преди %s седмици","след %s седмици"],["преди 1 месец","след 1 месец"],["преди %s месеца","след %s месеца"],["преди 1 година","след 1 година"],["преди %s години","след %s години"]][n]},bn_IN:function(s,n){return[["এইমাত্র","একটা সময়"],["%s সেকেন্ড আগে","%s এর সেকেন্ডের মধ্যে"],["1 মিনিট আগে","1 মিনিটে"],["%s এর মিনিট আগে","%s এর মিনিটের মধ্যে"],["1 ঘন্টা আগে","1 ঘন্টা"],["%s ঘণ্টা আগে","%s এর ঘন্টার মধ্যে"],["1 দিন আগে","1 দিনের মধ্যে"],["%s এর দিন আগে","%s এর দিন"],["1 সপ্তাহ আগে","1 সপ্তাহের মধ্যে"],["%s এর সপ্তাহ আগে","%s সপ্তাহের মধ্যে"],["1 মাস আগে","1 মাসে"],["%s মাস আগে","%s মাসে"],["1 বছর আগে","1 বছরের মধ্যে"],["%s বছর আগে","%s বছরে"]][n]},ca:function(s,n){return[["fa un moment","d'aquí un moment"],["fa %s segons","d'aquí %s segons"],["fa 1 minut","d'aquí 1 minut"],["fa %s minuts","d'aquí %s minuts"],["fa 1 hora","d'aquí 1 hora"],["fa %s hores","d'aquí %s hores"],["fa 1 dia","d'aquí 1 dia"],["fa %s dies","d'aquí %s dies"],["fa 1 setmana","d'aquí 1 setmana"],["fa %s setmanes","d'aquí %s setmanes"],["fa 1 mes","d'aquí 1 mes"],["fa %s mesos","d'aquí %s mesos"],["fa 1 any","d'aquí 1 any"],["fa %s anys","d'aquí %s anys"]][n]},de:function(s,n){return[["gerade eben","vor einer Weile"],["vor %s Sekunden","in %s Sekunden"],["vor 1 Minute","in 1 Minute"],["vor %s Minuten","in %s Minuten"],["vor 1 Stunde","in 1 Stunde"],["vor %s Stunden","in %s Stunden"],["vor 1 Tag","in 1 Tag"],["vor %s Tagen","in %s Tagen"],["vor 1 Woche","in 1 Woche"],["vor %s Wochen","in %s Wochen"],["vor 1 Monat","in 1 Monat"],["vor %s Monaten","in %s Monaten"],["vor 1 Jahr","in 1 Jahr"],["vor %s Jahren","in %s Jahren"]][n]},el:function(s,n){return[["μόλις τώρα","σε λίγο"],["%s δευτερόλεπτα πριν","σε %s δευτερόλεπτα"],["1 λεπτό πριν","σε 1 λεπτό"],["%s λεπτά πριν","σε %s λεπτά"],["1 ώρα πριν","σε 1 ώρα"],["%s ώρες πριν","σε %s ώρες"],["1 μέρα πριν","σε 1 μέρα"],["%s μέρες πριν","σε %s μέρες"],["1 εβδομάδα πριν","σε 1 εβδομάδα"],["%s εβδομάδες πριν","σε %s εβδομάδες"],["1 μήνα πριν","σε 1 μήνα"],["%s μήνες πριν","σε %s μήνες"],["1 χρόνο πριν","σε 1 χρόνο"],["%s χρόνια πριν","σε %s χρόνια"]][n]},en_short:function(s,n){return[["just now","right now"],["%ss ago","in %ss"],["1m ago","in 1m"],["%sm ago","in %sm"],["1h ago","in 1h"],["%sh ago","in %sh"],["1d ago","in 1d"],["%sd ago","in %sd"],["1w ago","in 1w"],["%sw ago","in %sw"],["1mo ago","in 1mo"],["%smo ago","in %smo"],["1yr ago","in 1yr"],["%syr ago","in %syr"]][n]},en_US:n,es:function(s,n){return[["justo ahora","en un rato"],["hace %s segundos","en %s segundos"],["hace 1 minuto","en 1 minuto"],["hace %s minutos","en %s minutos"],["hace 1 hora","en 1 hora"],["hace %s horas","en %s horas"],["hace 1 día","en 1 día"],["hace %s días","en %s días"],["hace 1 semana","en 1 semana"],["hace %s semanas","en %s semanas"],["hace 1 mes","en 1 mes"],["hace %s meses","en %s meses"],["hace 1 año","en 1 año"],["hace %s años","en %s años"]][n]},eu:function(s,n){return[["orain","denbora bat barru"],["duela %s segundu","%s segundu barru"],["duela minutu 1","minutu 1 barru"],["duela %s minutu","%s minutu barru"],["duela ordu 1","ordu 1 barru"],["duela %s ordu","%s ordu barru"],["duela egun 1","egun 1 barru"],["duela %s egun","%s egun barru"],["duela aste 1","aste 1 barru"],["duela %s aste","%s aste barru"],["duela hillabete 1","hillabete 1 barru"],["duela %s hillabete","%s hillabete barru"],["duela urte 1","urte 1 barru"],["duela %s urte","%s urte barru"]][n]},fa:function(s,n){var e=[["لحظاتی پیش","همین حالا"],["%s ثانیه پیش","%s ثانیه دیگر"],["۱ دقیقه پیش","۱ دقیقه دیگر"],["%s دقیقه پیش","%s دقیقه دیگر"],["۱ ساعت پیش","۱ ساعت دیگر"],["%s ساعت پیش","%s ساعت دیگر"],["۱ روز پیش","۱ روز دیگر"],["%s روز پیش","%s روز دیگر"],["۱ هفته پیش","۱ هفته دیگر"],["%s هفته پیش","%s هفته دیگر"],["۱ ماه پیش","۱ ماه دیگر"],["%s ماه پیش","%s ماه دیگر"],["۱ سال پیش","۱ سال دیگر"],["%s سال پیش","%s سال دیگر"]][n];return[e[0].replace("%s",M(s)),e[1].replace("%s",M(s))]},fi:function(s,n){return[["juuri äsken","juuri nyt"],["%s sekuntia sitten","%s sekunnin päästä"],["minuutti sitten","minuutin päästä"],["%s minuuttia sitten","%s minuutin päästä"],["tunti sitten","tunnin päästä"],["%s tuntia sitten","%s tunnin päästä"],["päivä sitten","päivän päästä"],["%s päivää sitten","%s päivän päästä"],["viikko sitten","viikon päästä"],["%s viikkoa sitten","%s viikon päästä"],["kuukausi sitten","kuukauden päästä"],["%s kuukautta sitten","%s kuukauden päästä"],["vuosi sitten","vuoden päästä"],["%s vuotta sitten","%s vuoden päästä"]][n]},fr:function(s,n){return[["à l'instant","dans un instant"],["il y a %s secondes","dans %s secondes"],["il y a 1 minute","dans 1 minute"],["il y a %s minutes","dans %s minutes"],["il y a 1 heure","dans 1 heure"],["il y a %s heures","dans %s heures"],["il y a 1 jour","dans 1 jour"],["il y a %s jours","dans %s jours"],["il y a 1 semaine","dans 1 semaine"],["il y a %s semaines","dans %s semaines"],["il y a 1 mois","dans 1 mois"],["il y a %s mois","dans %s mois"],["il y a 1 an","dans 1 an"],["il y a %s ans","dans %s ans"]][n]},gl:function(s,n){return[["xusto agora","daquí a un pouco"],["hai %s segundos","en %s segundos"],["hai 1 minuto","nun minuto"],["hai %s minutos","en %s minutos"],["hai 1 hora","nunha hora"],["hai %s horas","en %s horas"],["hai 1 día","nun día"],["hai %s días","en %s días"],["hai 1 semana","nunha semana"],["hai %s semanas","en %s semanas"],["hai 1 mes","nun mes"],["hai %s meses","en %s meses"],["hai 1 ano","nun ano"],["hai %s anos","en %s anos"]][n]},he:function(s,n){return[["זה עתה","עכשיו"],["לפני %s שניות","בעוד %s שניות"],["לפני דקה","בעוד דקה"],["לפני %s דקות","בעוד %s דקות"],["לפני שעה","בעוד שעה"],2===s?["לפני שעתיים","בעוד שעתיים"]:["לפני %s שעות","בעוד %s שעות"],["אתמול","מחר"],2===s?["לפני יומיים","בעוד יומיים"]:["לפני %s ימים","בעוד %s ימים"],["לפני שבוע","בעוד שבוע"],2===s?["לפני שבועיים","בעוד שבועיים"]:["לפני %s שבועות","בעוד %s שבועות"],["לפני חודש","בעוד חודש"],2===s?["לפני חודשיים","בעוד חודשיים"]:["לפני %s חודשים","בעוד %s חודשים"],["לפני שנה","בעוד שנה"],2===s?["לפני שנתיים","בעוד שנתיים"]:["לפני %s שנים","בעוד %s שנים"]][n]},hi_IN:function(s,n){return[["अभी","कुछ समय"],["%s सेकंड पहले","%s सेकंड में"],["1 मिनट पहले","1 मिनट में"],["%s मिनट पहले","%s मिनट में"],["1 घंटे पहले","1 घंटे में"],["%s घंटे पहले","%s घंटे में"],["1 दिन पहले","1 दिन में"],["%s दिन पहले","%s दिनों में"],["1 सप्ताह पहले","1 सप्ताह में"],["%s हफ्ते पहले","%s हफ्तों में"],["1 महीने पहले","1 महीने में"],["%s महीने पहले","%s महीनों में"],["1 साल पहले","1 साल में"],["%s साल पहले","%s साल में"]][n]},hu:function(s,n){return[["éppen most","éppen most"],["%s másodperce","%s másodpercen belül"],["1 perce","1 percen belül"],["%s perce","%s percen belül"],["1 órája","1 órán belül"],["%s órája","%s órán belül"],["1 napja","1 napon belül"],["%s napja","%s napon belül"],["1 hete","1 héten belül"],["%s hete","%s héten belül"],["1 hónapja","1 hónapon belül"],["%s hónapja","%s hónapon belül"],["1 éve","1 éven belül"],["%s éve","%s éven belül"]][n]},id_ID:function(s,n){return[["baru saja","sebentar"],["%s detik yang lalu","dalam %s detik"],["1 menit yang lalu","dalam 1 menit"],["%s menit yang lalu","dalam %s menit"],["1 jam yang lalu","dalam 1 jam"],["%s jam yang lalu","dalam %s jam"],["1 hari yang lalu","dalam 1 hari"],["%s hari yang lalu","dalam %s hari"],["1 minggu yang lalu","dalam 1 minggu"],["%s minggu yang lalu","dalam %s minggu"],["1 bulan yang lalu","dalam 1 bulan"],["%s bulan yang lalu","dalam %s bulan"],["1 tahun yang lalu","dalam 1 tahun"],["%s tahun yang lalu","dalam %s tahun"]][n]},it:function(s,n){return[["poco fa","fra poco"],["%s secondi fa","fra %s secondi"],["un minuto fa","fra un minuto"],["%s minuti fa","fra %s minuti"],["un'ora fa","fra un'ora"],["%s ore fa","fra %s ore"],["un giorno fa","fra un giorno"],["%s giorni fa","fra %s giorni"],["una settimana fa","fra una settimana"],["%s settimane fa","fra %s settimane"],["un mese fa","fra un mese"],["%s mesi fa","fra %s mesi"],["un anno fa","fra un anno"],["%s anni fa","fra %s anni"]][n]},ja:function(s,n){return[["すこし前","すぐに"],["%s秒前","%s秒以内"],["1分前","1分以内"],["%s分前","%s分以内"],["1時間前","1時間以内"],["%s時間前","%s時間以内"],["1日前","1日以内"],["%s日前","%s日以内"],["1週間前","1週間以内"],["%s週間前","%s週間以内"],["1ヶ月前","1ヶ月以内"],["%sヶ月前","%sヶ月以内"],["1年前","1年以内"],["%s年前","%s年以内"]][n]},ko:function(s,n){return[["방금","곧"],["%s초 전","%s초 후"],["1분 전","1분 후"],["%s분 전","%s분 후"],["1시간 전","1시간 후"],["%s시간 전","%s시간 후"],["1일 전","1일 후"],["%s일 전","%s일 후"],["1주일 전","1주일 후"],["%s주일 전","%s주일 후"],["1개월 전","1개월 후"],["%s개월 전","%s개월 후"],["1년 전","1년 후"],["%s년 전","%s년 후"]][n]},ml:function(s,n){return[["ഇപ്പോള്‍","കുറച്ചു മുന്‍പ്"],["%s സെക്കന്റ്‌കള്‍ക്ക് മുന്‍പ്","%s സെക്കന്റില്‍"],["1 മിനിറ്റിനു മുന്‍പ്","1 മിനിറ്റില്‍"],["%s മിനിറ്റുകള്‍ക്ക് മുന്‍പ","%s മിനിറ്റില്‍"],["1 മണിക്കൂറിനു മുന്‍പ്","1 മണിക്കൂറില്‍"],["%s മണിക്കൂറുകള്‍ക്കു മുന്‍പ്","%s മണിക്കൂറില്‍"],["1 ഒരു ദിവസം മുന്‍പ്","1 ദിവസത്തില്‍"],["%s ദിവസങ്ങള്‍ക് മുന്‍പ്","%s ദിവസങ്ങള്‍ക്കുള്ളില്‍"],["1 ആഴ്ച മുന്‍പ്","1 ആഴ്ചയില്‍"],["%s ആഴ്ചകള്‍ക്ക് മുന്‍പ്","%s ആഴ്ചകള്‍ക്കുള്ളില്‍"],["1 മാസത്തിനു മുന്‍പ്","1 മാസത്തിനുള്ളില്‍"],["%s മാസങ്ങള്‍ക്ക് മുന്‍പ്","%s മാസങ്ങള്‍ക്കുള്ളില്‍"],["1 വര്‍ഷത്തിനു  മുന്‍പ്","1 വര്‍ഷത്തിനുള്ളില്‍"],["%s വര്‍ഷങ്ങള്‍ക്കു മുന്‍പ്","%s വര്‍ഷങ്ങള്‍ക്കുല്ല്ളില്‍"]][n]},my:function(s,n){return[["ယခုအတွင်း","ယခု"],["%s စက္ကန့် အကြာက","%s စက္ကန့်အတွင်း"],["1 မိနစ် အကြာက","1 မိနစ်အတွင်း"],["%s မိနစ် အကြာက","%s မိနစ်အတွင်း"],["1 နာရီ အကြာက","1 နာရီအတွင်း"],["%s နာရီ အကြာက","%s နာရီအတွင်း"],["1 ရက် အကြာက","1 ရက်အတွင်း"],["%s ရက် အကြာက","%s ရက်အတွင်း"],["1 ပတ် အကြာက","1 ပတ်အတွင်း"],["%s ပတ် အကြာက","%s ပတ်အတွင်း"],["1 လ အကြာက","1 လအတွင်း"],["%s လ အကြာက","%s လအတွင်း"],["1 နှစ် အကြာက","1 နှစ်အတွင်း"],["%s နှစ် အကြာက","%s နှစ်အတွင်း"]][n]},nb_NO:function(s,n){return[["akkurat nå","om litt"],["%s sekunder siden","om %s sekunder"],["1 minutt siden","om 1 minutt"],["%s minutter siden","om %s minutter"],["1 time siden","om 1 time"],["%s timer siden","om %s timer"],["1 dag siden","om 1 dag"],["%s dager siden","om %s dager"],["1 uke siden","om 1 uke"],["%s uker siden","om %s uker"],["1 måned siden","om 1 måned"],["%s måneder siden","om %s måneder"],["1 år siden","om 1 år"],["%s år siden","om %s år"]][n]},nl:function(s,n){return[["recent","binnenkort"],["%s seconden geleden","binnen %s seconden"],["1 minuut geleden","binnen 1 minuut"],["%s minuten geleden","binnen %s minuten"],["1 uur geleden","binnen 1 uur"],["%s uur geleden","binnen %s uur"],["1 dag geleden","binnen 1 dag"],["%s dagen geleden","binnen %s dagen"],["1 week geleden","binnen 1 week"],["%s weken geleden","binnen %s weken"],["1 maand geleden","binnen 1 maand"],["%s maanden geleden","binnen %s maanden"],["1 jaar geleden","binnen 1 jaar"],["%s jaar geleden","binnen %s jaar"]][n]},nn_NO:function(s,n){return[["nett no","om litt"],["%s sekund sidan","om %s sekund"],["1 minutt sidan","om 1 minutt"],["%s minutt sidan","om %s minutt"],["1 time sidan","om 1 time"],["%s timar sidan","om %s timar"],["1 dag sidan","om 1 dag"],["%s dagar sidan","om %s dagar"],["1 veke sidan","om 1 veke"],["%s veker sidan","om %s veker"],["1 månad sidan","om 1 månad"],["%s månadar sidan","om %s månadar"],["1 år sidan","om 1 år"],["%s år sidan","om %s år"]][n]},pl:function(s,n){return S[1&n?4<s%10||s%10<2||1==~~(s/10)%10?n:++n/2+13:n]},pt_BR:function(s,n){return[["agora mesmo","agora"],["há %s segundos","em %s segundos"],["há um minuto","em um minuto"],["há %s minutos","em %s minutos"],["há uma hora","em uma hora"],["há %s horas","em %s horas"],["há um dia","em um dia"],["há %s dias","em %s dias"],["há uma semana","em uma semana"],["há %s semanas","em %s semanas"],["há um mês","em um mês"],["há %s meses","em %s meses"],["há um ano","em um ano"],["há %s anos","em %s anos"]][n]},ro:function(s,n){var e=[["chiar acum","chiar acum"],["acum %s secunde","peste %s secunde"],["acum un minut","peste un minut"],["acum %s minute","peste %s minute"],["acum o oră","peste o oră"],["acum %s ore","peste %s ore"],["acum o zi","peste o zi"],["acum %s zile","peste %s zile"],["acum o săptămână","peste o săptămână"],["acum %s săptămâni","peste %s săptămâni"],["acum o lună","peste o lună"],["acum %s luni","peste %s luni"],["acum un an","peste un an"],["acum %s ani","peste %s ani"]];return s<20?e[n]:[e[n][0].replace("%s","%s de"),e[n][1].replace("%s","%s de")]},ru:function(s,n){switch(n){case 0:return["только что","через несколько секунд"];case 1:return[N(s)+" назад","через "+N(s)];case 2:case 3:return[x(s)+" назад","через "+x(s)];case 4:case 5:return[D(s)+" назад","через "+D(s)];case 6:return["вчера","завтра"];case 7:return[I(s)+" назад","через "+I(s)];case 8:case 9:return[O(s)+" назад","через "+O(s)];case 10:case 11:return[W(s)+" назад","через "+W(s)];case 12:case 13:return[$(s)+" назад","через "+$(s)];default:return["",""]}},sq:function(s,n){return[["pak më parë","pas pak"],["para %s sekondash","pas %s sekondash"],["para një minute","pas një minute"],["para %s minutash","pas %s minutash"],["para një ore","pas një ore"],["para %s orësh","pas %s orësh"],["dje","nesër"],["para %s ditësh","pas %s ditësh"],["para një jave","pas një jave"],["para %s javësh","pas %s javësh"],["para një muaji","pas një muaji"],["para %s muajsh","pas %s muajsh"],["para një viti","pas një viti"],["para %s vjetësh","pas %s vjetësh"]][n]},sr:function(s,n){switch(n){case 0:return["малопре","управо сад"];case 1:return["пре "+U(s),"за "+U(s)];case 2:case 3:return["пре "+A(s),"за "+A(s)];case 4:case 5:return["пре "+C(s),"за "+C(s)];case 6:case 7:return["пре "+E(s),"за "+E(s)];case 8:case 9:return["пре "+B(s),"за "+B(s)];case 10:case 11:return["пре "+P(s),"за "+P(s)];case 12:case 13:return["пре "+R(s),"за "+R(s)];default:return["",""]}},sv:function(s,n){return[["just nu","om en stund"],["%s sekunder sedan","om %s sekunder"],["1 minut sedan","om 1 minut"],["%s minuter sedan","om %s minuter"],["1 timme sedan","om 1 timme"],["%s timmar sedan","om %s timmar"],["1 dag sedan","om 1 dag"],["%s dagar sedan","om %s dagar"],["1 vecka sedan","om 1 vecka"],["%s veckor sedan","om %s veckor"],["1 månad sedan","om 1 månad"],["%s månader sedan","om %s månader"],["1 år sedan","om 1 år"],["%s år sedan","om %s år"]][n]},ta:function(s,n){return[["இப்போது","சற்று நேரம் முன்பு"],["%s நொடிக்கு முன்","%s நொடிகளில்"],["1 நிமிடத்திற்க்கு முன்","1 நிமிடத்தில்"],["%s நிமிடத்திற்க்கு முன்","%s நிமிடங்களில்"],["1 மணி நேரத்திற்கு முன்","1 மணி நேரத்திற்குள்"],["%s மணி நேரத்திற்கு முன்","%s மணி நேரத்திற்குள்"],["1 நாளுக்கு முன்","1 நாளில்"],["%s நாட்களுக்கு முன்","%s நாட்களில்"],["1 வாரத்திற்கு முன்","1 வாரத்தில்"],["%s வாரங்களுக்கு முன்","%s வாரங்களில்"],["1 மாதத்திற்கு முன்","1 மாதத்தில்"],["%s மாதங்களுக்கு முன்","%s மாதங்களில்"],["1 வருடத்திற்கு முன்","1 வருடத்தில்"],["%s வருடங்களுக்கு முன்","%s வருடங்களில்"]][n]},th:function(s,n){return[["เมื่อสักครู่นี้","อีกสักครู่"],["%s วินาทีที่แล้ว","ใน %s วินาที"],["1 นาทีที่แล้ว","ใน 1 นาที"],["%s นาทีที่แล้ว","ใน %s นาที"],["1 ชั่วโมงที่แล้ว","ใน 1 ชั่วโมง"],["%s ชั่วโมงที่แล้ว","ใน %s ชั่วโมง"],["1 วันที่แล้ว","ใน 1 วัน"],["%s วันที่แล้ว","ใน %s วัน"],["1 อาทิตย์ที่แล้ว","ใน 1 อาทิตย์"],["%s อาทิตย์ที่แล้ว","ใน %s อาทิตย์"],["1 เดือนที่แล้ว","ใน 1 เดือน"],["%s เดือนที่แล้ว","ใน %s เดือน"],["1 ปีที่แล้ว","ใน 1 ปี"],["%s ปีที่แล้ว","ใน %s ปี"]][n]},tr:function(s,n){return[["az önce","şimdi"],["%s saniye önce","%s saniye içinde"],["1 dakika önce","1 dakika içinde"],["%s dakika önce","%s dakika içinde"],["1 saat önce","1 saat içinde"],["%s saat önce","%s saat içinde"],["1 gün önce","1 gün içinde"],["%s gün önce","%s gün içinde"],["1 hafta önce","1 hafta içinde"],["%s hafta önce","%s hafta içinde"],["1 ay önce","1 ay içinde"],["%s ay önce","%s ay içinde"],["1 yıl önce","1 yıl içinde"],["%s yıl önce","%s yıl içinde"]][n]},uk:function(s,n){switch(n){case 0:return["щойно","через декілька секунд"];case 1:return[F(s)+" тому","через "+F(s)];case 2:case 3:return[G(s)+" тому","через "+G(s)];case 4:case 5:return[H(s)+" тому","через "+H(s)];case 6:case 7:return[K(s)+" тому","через "+K(s)];case 8:case 9:return[L(s)+" тому","через "+L(s)];case 10:case 11:return[Q(s)+" тому","через "+Q(s)];case 12:case 13:return[V(s)+" тому","через "+V(s)];default:return["",""]}},vi:function(s,n){return[["vừa xong","một lúc"],["%s giây trước","trong %s giây"],["1 phút trước","trong 1 phút"],["%s phút trước","trong %s phút"],["1 giờ trước","trong 1 giờ"],["%s giờ trước","trong %s giờ"],["1 ngày trước","trong 1 ngày"],["%s ngày trước","trong %s ngày"],["1 tuần trước","trong 1 tuần"],["%s tuần trước","trong %s tuần"],["1 tháng trước","trong 1 tháng"],["%s tháng trước","trong %s tháng"],["1 năm trước","trong 1 năm"],["%s năm trước","trong %s năm"]][n]},zh_CN:e,zh_TW:function(s,n){return[["剛剛","片刻後"],["%s 秒前","%s 秒後"],["1 分鐘前","1 分鐘後"],["%s 分鐘前","%s 分鐘後"],["1 小時前","1 小時後"],["%s 小時前","%s 小時後"],["1 天前","1 天後"],["%s 天前","%s 天後"],["1 週前","1 週後"],["%s 週前","%s 週後"],["1 個月前","1 個月後"],["%s 個月前","%s 個月後"],["1 年前","1 年後"],["%s 年前","%s 年後"]][n]}});Object.keys(X).forEach(function(s){u(s,X[s])}),s.cancel=function(s){s?f(h(s)):Object.keys(g).forEach(f)},s.format=function(s,n,e){return d(c(s,e&&e.relativeDate),r(n))},s.register=u,s.render=function(s,n,e){var a=s.length?s:[s];return a.forEach(function(s){p(s,function(s){return s.getAttribute("datetime")}(s),r(n),e||{})}),a},Object.defineProperty(s,"__esModule",{value:!0})});
;
/*! X-editable - v1.5.1 
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
* http://github.com/vitalets/x-editable
* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
!function(a){"use strict";var b=function(b,c){this.options=a.extend({},a.fn.editableform.defaults,c),this.$div=a(b),this.options.scope||(this.options.scope=this)};b.prototype={constructor:b,initInput:function(){this.input=this.options.input,this.value=this.input.str2value(this.options.value),this.input.prerender()},initTemplate:function(){this.$form=a(a.fn.editableform.template)},initButtons:function(){var b=this.$form.find(".editable-buttons");b.append(a.fn.editableform.buttons),"bottom"===this.options.showbuttons&&b.addClass("editable-buttons-bottom")},render:function(){this.$loading=a(a.fn.editableform.loading),this.$div.empty().append(this.$loading),this.initTemplate(),this.options.showbuttons?this.initButtons():this.$form.find(".editable-buttons").remove(),this.showLoading(),this.isSaving=!1,this.$div.triggerHandler("rendering"),this.initInput(),this.$form.find("div.editable-input").append(this.input.$tpl),this.$div.append(this.$form),a.when(this.input.render()).then(a.proxy(function(){if(this.options.showbuttons||this.input.autosubmit(),this.$form.find(".editable-cancel").click(a.proxy(this.cancel,this)),this.input.error)this.error(this.input.error),this.$form.find(".editable-submit").attr("disabled",!0),this.input.$input.attr("disabled",!0),this.$form.submit(function(a){a.preventDefault()});else{this.error(!1),this.input.$input.removeAttr("disabled"),this.$form.find(".editable-submit").removeAttr("disabled");var b=null===this.value||void 0===this.value||""===this.value?this.options.defaultValue:this.value;this.input.value2input(b),this.$form.submit(a.proxy(this.submit,this))}this.$div.triggerHandler("rendered"),this.showForm(),this.input.postrender&&this.input.postrender()},this))},cancel:function(){this.$div.triggerHandler("cancel")},showLoading:function(){var a,b;this.$form?(a=this.$form.outerWidth(),b=this.$form.outerHeight(),a&&this.$loading.width(a),b&&this.$loading.height(b),this.$form.hide()):(a=this.$loading.parent().width(),a&&this.$loading.width(a)),this.$loading.show()},showForm:function(a){this.$loading.hide(),this.$form.show(),a!==!1&&this.input.activate(),this.$div.triggerHandler("show")},error:function(b){var c,d=this.$form.find(".control-group"),e=this.$form.find(".editable-error-block");if(b===!1)d.removeClass(a.fn.editableform.errorGroupClass),e.removeClass(a.fn.editableform.errorBlockClass).empty().hide();else{if(b){c=(""+b).split("\n");for(var f=0;f<c.length;f++)c[f]=a("<div>").text(c[f]).html();b=c.join("<br>")}d.addClass(a.fn.editableform.errorGroupClass),e.addClass(a.fn.editableform.errorBlockClass).html(b).show()}},submit:function(b){b.stopPropagation(),b.preventDefault();var c=this.input.input2value(),d=this.validate(c);if("object"===a.type(d)&&void 0!==d.newValue){if(c=d.newValue,this.input.value2input(c),"string"==typeof d.msg)return this.error(d.msg),this.showForm(),void 0}else if(d)return this.error(d),this.showForm(),void 0;if(!this.options.savenochange&&this.input.value2str(c)==this.input.value2str(this.value))return this.$div.triggerHandler("nochange"),void 0;var e=this.input.value2submit(c);this.isSaving=!0,a.when(this.save(e)).done(a.proxy(function(a){this.isSaving=!1;var b="function"==typeof this.options.success?this.options.success.call(this.options.scope,a,c):null;return b===!1?(this.error(!1),this.showForm(!1),void 0):"string"==typeof b?(this.error(b),this.showForm(),void 0):(b&&"object"==typeof b&&b.hasOwnProperty("newValue")&&(c=b.newValue),this.error(!1),this.value=c,this.$div.triggerHandler("save",{newValue:c,submitValue:e,response:a}),void 0)},this)).fail(a.proxy(function(a){this.isSaving=!1;var b;b="function"==typeof this.options.error?this.options.error.call(this.options.scope,a,c):"string"==typeof a?a:a.responseText||a.statusText||"Unknown error!",this.error(b),this.showForm()},this))},save:function(b){this.options.pk=a.fn.editableutils.tryParseJson(this.options.pk,!0);var c,d="function"==typeof this.options.pk?this.options.pk.call(this.options.scope):this.options.pk,e=!!("function"==typeof this.options.url||this.options.url&&("always"===this.options.send||"auto"===this.options.send&&null!==d&&void 0!==d));return e?(this.showLoading(),c={name:this.options.name||"",value:b,pk:d},"function"==typeof this.options.params?c=this.options.params.call(this.options.scope,c):(this.options.params=a.fn.editableutils.tryParseJson(this.options.params,!0),a.extend(c,this.options.params)),"function"==typeof this.options.url?this.options.url.call(this.options.scope,c):a.ajax(a.extend({url:this.options.url,data:c,type:"POST"},this.options.ajaxOptions))):void 0},validate:function(a){return void 0===a&&(a=this.value),"function"==typeof this.options.validate?this.options.validate.call(this.options.scope,a):void 0},option:function(a,b){a in this.options&&(this.options[a]=b),"value"===a&&this.setValue(b)},setValue:function(a,b){this.value=b?this.input.str2value(a):a,this.$form&&this.$form.is(":visible")&&this.input.value2input(this.value)}},a.fn.editableform=function(c){var d=arguments;return this.each(function(){var e=a(this),f=e.data("editableform"),g="object"==typeof c&&c;f||e.data("editableform",f=new b(this,g)),"string"==typeof c&&f[c].apply(f,Array.prototype.slice.call(d,1))})},a.fn.editableform.Constructor=b,a.fn.editableform.defaults={type:"text",url:null,params:null,name:null,pk:null,value:null,defaultValue:null,send:"auto",validate:null,success:null,error:null,ajaxOptions:null,showbuttons:!0,scope:null,savenochange:!1},a.fn.editableform.template='<form class="form-inline editableform"><div class="control-group"><div><div class="editable-input"></div><div class="editable-buttons"></div></div><div class="editable-error-block"></div></div></form>',a.fn.editableform.loading='<div class="editableform-loading"></div>',a.fn.editableform.buttons='<button type="submit" class="editable-submit">ok</button><button type="button" class="editable-cancel">cancel</button>',a.fn.editableform.errorGroupClass=null,a.fn.editableform.errorBlockClass="editable-error",a.fn.editableform.engine="jquery"}(window.jQuery),function(a){"use strict";a.fn.editableutils={inherit:function(a,b){var c=function(){};c.prototype=b.prototype,a.prototype=new c,a.prototype.constructor=a,a.superclass=b.prototype},setCursorPosition:function(a,b){if(a.setSelectionRange)a.setSelectionRange(b,b);else if(a.createTextRange){var c=a.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",b),c.select()}},tryParseJson:function(a,b){if("string"==typeof a&&a.length&&a.match(/^[\{\[].*[\}\]]$/))if(b)try{a=new Function("return "+a)()}catch(c){}finally{return a}else a=new Function("return "+a)();return a},sliceObj:function(b,c,d){var e,f,g={};if(!a.isArray(c)||!c.length)return g;for(var h=0;h<c.length;h++)e=c[h],b.hasOwnProperty(e)&&(g[e]=b[e]),d!==!0&&(f=e.toLowerCase(),b.hasOwnProperty(f)&&(g[e]=b[f]));return g},getConfigData:function(b){var c={};return a.each(b.data(),function(a,b){("object"!=typeof b||b&&"object"==typeof b&&(b.constructor===Object||b.constructor===Array))&&(c[a]=b)}),c},objectKeys:function(a){if(Object.keys)return Object.keys(a);if(a!==Object(a))throw new TypeError("Object.keys called on a non-object");var b,c=[];for(b in a)Object.prototype.hasOwnProperty.call(a,b)&&c.push(b);return c},escape:function(b){return a("<div>").text(b).html()},itemsByValue:function(b,c,d){if(!c||null===b)return[];if("function"!=typeof d){var e=d||"value";d=function(a){return a[e]}}var f=a.isArray(b),g=[],h=this;return a.each(c,function(c,e){if(e.children)g=g.concat(h.itemsByValue(b,e.children,d));else if(f)a.grep(b,function(a){return a==(e&&"object"==typeof e?d(e):e)}).length&&g.push(e);else{var i=e&&"object"==typeof e?d(e):e;b==i&&g.push(e)}}),g},createInput:function(b){var c,d,e,f=b.type;return"date"===f&&("inline"===b.mode?a.fn.editabletypes.datefield?f="datefield":a.fn.editabletypes.dateuifield&&(f="dateuifield"):a.fn.editabletypes.date?f="date":a.fn.editabletypes.dateui&&(f="dateui"),"date"!==f||a.fn.editabletypes.date||(f="combodate")),"datetime"===f&&"inline"===b.mode&&(f="datetimefield"),"wysihtml5"!==f||a.fn.editabletypes[f]||(f="textarea"),"function"==typeof a.fn.editabletypes[f]?(c=a.fn.editabletypes[f],d=this.sliceObj(b,this.objectKeys(c.defaults)),e=new c(d)):(a.error("Unknown type: "+f),!1)},supportsTransitions:function(){var a=document.body||document.documentElement,b=a.style,c="transition",d=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof b[c])return!0;c=c.charAt(0).toUpperCase()+c.substr(1);for(var e=0;e<d.length;e++)if("string"==typeof b[d[e]+c])return!0;return!1}}}(window.jQuery),function(a){"use strict";var b=function(a,b){this.init(a,b)},c=function(a,b){this.init(a,b)};b.prototype={containerName:null,containerDataName:null,innerCss:null,containerClass:"editable-container editable-popup",defaults:{},init:function(c,d){this.$element=a(c),this.options=a.extend({},a.fn.editableContainer.defaults,d),this.splitOptions(),this.formOptions.scope=this.$element[0],this.initContainer(),this.delayedHide=!1,this.$element.on("destroyed",a.proxy(function(){this.destroy()},this)),a(document).data("editable-handlers-attached")||(a(document).on("keyup.editable",function(b){27===b.which&&a(".editable-open").editableContainer("hide")}),a(document).on("click.editable",function(c){var d,e=a(c.target),f=[".editable-container",".ui-datepicker-header",".datepicker",".modal-backdrop",".bootstrap-wysihtml5-insert-image-modal",".bootstrap-wysihtml5-insert-link-modal"];if(a.contains(document.documentElement,c.target)&&!e.is(document)){for(d=0;d<f.length;d++)if(e.is(f[d])||e.parents(f[d]).length)return;b.prototype.closeOthers(c.target)}}),a(document).data("editable-handlers-attached",!0))},splitOptions:function(){if(this.containerOptions={},this.formOptions={},!a.fn[this.containerName])throw new Error(this.containerName+" not found. Have you included corresponding js file?");for(var b in this.options)b in this.defaults?this.containerOptions[b]=this.options[b]:this.formOptions[b]=this.options[b]},tip:function(){return this.container()?this.container().$tip:null},container:function(){var a;return this.containerDataName&&(a=this.$element.data(this.containerDataName))?a:a=this.$element.data(this.containerName)},call:function(){this.$element[this.containerName].apply(this.$element,arguments)},initContainer:function(){this.call(this.containerOptions)},renderForm:function(){this.$form.editableform(this.formOptions).on({save:a.proxy(this.save,this),nochange:a.proxy(function(){this.hide("nochange")},this),cancel:a.proxy(function(){this.hide("cancel")},this),show:a.proxy(function(){this.delayedHide?(this.hide(this.delayedHide.reason),this.delayedHide=!1):this.setPosition()},this),rendering:a.proxy(this.setPosition,this),resize:a.proxy(this.setPosition,this),rendered:a.proxy(function(){this.$element.triggerHandler("shown",a(this.options.scope).data("editable"))},this)}).editableform("render")},show:function(b){this.$element.addClass("editable-open"),b!==!1&&this.closeOthers(this.$element[0]),this.innerShow(),this.tip().addClass(this.containerClass),this.$form,this.$form=a("<div>"),this.tip().is(this.innerCss)?this.tip().append(this.$form):this.tip().find(this.innerCss).append(this.$form),this.renderForm()},hide:function(a){if(this.tip()&&this.tip().is(":visible")&&this.$element.hasClass("editable-open")){if(this.$form.data("editableform").isSaving)return this.delayedHide={reason:a},void 0;this.delayedHide=!1,this.$element.removeClass("editable-open"),this.innerHide(),this.$element.triggerHandler("hidden",a||"manual")}},innerShow:function(){},innerHide:function(){},toggle:function(a){this.container()&&this.tip()&&this.tip().is(":visible")?this.hide():this.show(a)},setPosition:function(){},save:function(a,b){this.$element.triggerHandler("save",b),this.hide("save")},option:function(a,b){this.options[a]=b,a in this.containerOptions?(this.containerOptions[a]=b,this.setContainerOption(a,b)):(this.formOptions[a]=b,this.$form&&this.$form.editableform("option",a,b))},setContainerOption:function(a,b){this.call("option",a,b)},destroy:function(){this.hide(),this.innerDestroy(),this.$element.off("destroyed"),this.$element.removeData("editableContainer")},innerDestroy:function(){},closeOthers:function(b){a(".editable-open").each(function(c,d){if(d!==b&&!a(d).find(b).length){var e=a(d),f=e.data("editableContainer");f&&("cancel"===f.options.onblur?e.data("editableContainer").hide("onblur"):"submit"===f.options.onblur&&e.data("editableContainer").tip().find("form").submit())}})},activate:function(){this.tip&&this.tip().is(":visible")&&this.$form&&this.$form.data("editableform").input.activate()}},a.fn.editableContainer=function(d){var e=arguments;return this.each(function(){var f=a(this),g="editableContainer",h=f.data(g),i="object"==typeof d&&d,j="inline"===i.mode?c:b;h||f.data(g,h=new j(this,i)),"string"==typeof d&&h[d].apply(h,Array.prototype.slice.call(e,1))})},a.fn.editableContainer.Popup=b,a.fn.editableContainer.Inline=c,a.fn.editableContainer.defaults={value:null,placement:"top",autohide:!0,onblur:"cancel",anim:!1,mode:"popup"},jQuery.event.special.destroyed={remove:function(a){a.handler&&a.handler()}}}(window.jQuery),function(a){"use strict";a.extend(a.fn.editableContainer.Inline.prototype,a.fn.editableContainer.Popup.prototype,{containerName:"editableform",innerCss:".editable-inline",containerClass:"editable-container editable-inline",initContainer:function(){this.$tip=a("<span></span>"),this.options.anim||(this.options.anim=0)},splitOptions:function(){this.containerOptions={},this.formOptions=this.options},tip:function(){return this.$tip},innerShow:function(){this.$element.hide(),this.tip().insertAfter(this.$element).show()},innerHide:function(){this.$tip.hide(this.options.anim,a.proxy(function(){this.$element.show(),this.innerDestroy()},this))},innerDestroy:function(){this.tip()&&this.tip().empty().remove()}})}(window.jQuery),function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.editable.defaults,c,a.fn.editableutils.getConfigData(this.$element)),this.options.selector?this.initLive():this.init(),this.options.highlight&&!a.fn.editableutils.supportsTransitions()&&(this.options.highlight=!1)};b.prototype={constructor:b,init:function(){var b,c=!1;if(this.options.name=this.options.name||this.$element.attr("id"),this.options.scope=this.$element[0],this.input=a.fn.editableutils.createInput(this.options),this.input){switch(void 0===this.options.value||null===this.options.value?(this.value=this.input.html2value(a.trim(this.$element.html())),c=!0):(this.options.value=a.fn.editableutils.tryParseJson(this.options.value,!0),this.value="string"==typeof this.options.value?this.input.str2value(this.options.value):this.options.value),this.$element.addClass("editable"),"textarea"===this.input.type&&this.$element.addClass("editable-pre-wrapped"),"manual"!==this.options.toggle?(this.$element.addClass("editable-click"),this.$element.on(this.options.toggle+".editable",a.proxy(function(a){if(this.options.disabled||a.preventDefault(),"mouseenter"===this.options.toggle)this.show();else{var b="click"!==this.options.toggle;this.toggle(b)}},this))):this.$element.attr("tabindex",-1),"function"==typeof this.options.display&&(this.options.autotext="always"),this.options.autotext){case"always":b=!0;break;case"auto":b=!a.trim(this.$element.text()).length&&null!==this.value&&void 0!==this.value&&!c;break;default:b=!1}a.when(b?this.render():!0).then(a.proxy(function(){this.options.disabled?this.disable():this.enable(),this.$element.triggerHandler("init",this)},this))}},initLive:function(){var b=this.options.selector;this.options.selector=!1,this.options.autotext="never",this.$element.on(this.options.toggle+".editable",b,a.proxy(function(b){var c=a(b.target);c.data("editable")||(c.hasClass(this.options.emptyclass)&&c.empty(),c.editable(this.options).trigger(b))},this))},render:function(a){return this.options.display!==!1?this.input.value2htmlFinal?this.input.value2html(this.value,this.$element[0],this.options.display,a):"function"==typeof this.options.display?this.options.display.call(this.$element[0],this.value,a):this.input.value2html(this.value,this.$element[0]):void 0},enable:function(){this.options.disabled=!1,this.$element.removeClass("editable-disabled"),this.handleEmpty(this.isEmpty),"manual"!==this.options.toggle&&"-1"===this.$element.attr("tabindex")&&this.$element.removeAttr("tabindex")},disable:function(){this.options.disabled=!0,this.hide(),this.$element.addClass("editable-disabled"),this.handleEmpty(this.isEmpty),this.$element.attr("tabindex",-1)},toggleDisabled:function(){this.options.disabled?this.enable():this.disable()},option:function(b,c){return b&&"object"==typeof b?(a.each(b,a.proxy(function(b,c){this.option(a.trim(b),c)},this)),void 0):(this.options[b]=c,"disabled"===b?c?this.disable():this.enable():("value"===b&&this.setValue(c),this.container&&this.container.option(b,c),this.input.option&&this.input.option(b,c),void 0))},handleEmpty:function(b){this.options.display!==!1&&(this.isEmpty=void 0!==b?b:"function"==typeof this.input.isEmpty?this.input.isEmpty(this.$element):""===a.trim(this.$element.html()),this.options.disabled?this.isEmpty&&(this.$element.empty(),this.options.emptyclass&&this.$element.removeClass(this.options.emptyclass)):this.isEmpty?(this.$element.html(this.options.emptytext),this.options.emptyclass&&this.$element.addClass(this.options.emptyclass)):this.options.emptyclass&&this.$element.removeClass(this.options.emptyclass))},show:function(b){if(!this.options.disabled){if(this.container){if(this.container.tip().is(":visible"))return}else{var c=a.extend({},this.options,{value:this.value,input:this.input});this.$element.editableContainer(c),this.$element.on("save.internal",a.proxy(this.save,this)),this.container=this.$element.data("editableContainer")}this.container.show(b)}},hide:function(){this.container&&this.container.hide()},toggle:function(a){this.container&&this.container.tip().is(":visible")?this.hide():this.show(a)},save:function(a,b){if(this.options.unsavedclass){var c=!1;c=c||"function"==typeof this.options.url,c=c||this.options.display===!1,c=c||void 0!==b.response,c=c||this.options.savenochange&&this.input.value2str(this.value)!==this.input.value2str(b.newValue),c?this.$element.removeClass(this.options.unsavedclass):this.$element.addClass(this.options.unsavedclass)}if(this.options.highlight){var d=this.$element,e=d.css("background-color");d.css("background-color",this.options.highlight),setTimeout(function(){"transparent"===e&&(e=""),d.css("background-color",e),d.addClass("editable-bg-transition"),setTimeout(function(){d.removeClass("editable-bg-transition")},1700)},10)}this.setValue(b.newValue,!1,b.response)},validate:function(){return"function"==typeof this.options.validate?this.options.validate.call(this,this.value):void 0},setValue:function(b,c,d){this.value=c?this.input.str2value(b):b,this.container&&this.container.option("value",this.value),a.when(this.render(d)).then(a.proxy(function(){this.handleEmpty()},this))},activate:function(){this.container&&this.container.activate()},destroy:function(){this.disable(),this.container&&this.container.destroy(),this.input.destroy(),"manual"!==this.options.toggle&&(this.$element.removeClass("editable-click"),this.$element.off(this.options.toggle+".editable")),this.$element.off("save.internal"),this.$element.removeClass("editable editable-open editable-disabled"),this.$element.removeData("editable")}},a.fn.editable=function(c){var d={},e=arguments,f="editable";switch(c){case"validate":return this.each(function(){var b,c=a(this),e=c.data(f);e&&(b=e.validate())&&(d[e.options.name]=b)}),d;case"getValue":return 2===arguments.length&&arguments[1]===!0?d=this.eq(0).data(f).value:this.each(function(){var b=a(this),c=b.data(f);c&&void 0!==c.value&&null!==c.value&&(d[c.options.name]=c.input.value2submit(c.value))}),d;case"submit":var g=arguments[1]||{},h=this,i=this.editable("validate");if(a.isEmptyObject(i)){var j={};if(1===h.length){var k=h.data("editable"),l={name:k.options.name||"",value:k.input.value2submit(k.value),pk:"function"==typeof k.options.pk?k.options.pk.call(k.options.scope):k.options.pk};"function"==typeof k.options.params?l=k.options.params.call(k.options.scope,l):(k.options.params=a.fn.editableutils.tryParseJson(k.options.params,!0),a.extend(l,k.options.params)),j={url:k.options.url,data:l,type:"POST"},g.success=g.success||k.options.success,g.error=g.error||k.options.error}else{var m=this.editable("getValue");j={url:g.url,data:m,type:"POST"}}j.success="function"==typeof g.success?function(a){g.success.call(h,a,g)}:a.noop,j.error="function"==typeof g.error?function(){g.error.apply(h,arguments)}:a.noop,g.ajaxOptions&&a.extend(j,g.ajaxOptions),g.data&&a.extend(j.data,g.data),a.ajax(j)}else"function"==typeof g.error&&g.error.call(h,i);return this}return this.each(function(){var d=a(this),g=d.data(f),h="object"==typeof c&&c;return h&&h.selector?(g=new b(this,h),void 0):(g||d.data(f,g=new b(this,h)),"string"==typeof c&&g[c].apply(g,Array.prototype.slice.call(e,1)),void 0)})},a.fn.editable.defaults={type:"text",disabled:!1,toggle:"click",emptytext:"Empty",autotext:"auto",value:null,display:null,emptyclass:"editable-empty",unsavedclass:"editable-unsaved",selector:null,highlight:"#FFFF80"}}(window.jQuery),function(a){"use strict";a.fn.editabletypes={};var b=function(){};b.prototype={init:function(b,c,d){this.type=b,this.options=a.extend({},d,c)},prerender:function(){this.$tpl=a(this.options.tpl),this.$input=this.$tpl,this.$clear=null,this.error=null},render:function(){},value2html:function(b,c){a(c)[this.options.escape?"text":"html"](a.trim(b))},html2value:function(b){return a("<div>").html(b).text()},value2str:function(a){return a},str2value:function(a){return a},value2submit:function(a){return a},value2input:function(a){this.$input.val(a)},input2value:function(){return this.$input.val()},activate:function(){this.$input.is(":visible")&&this.$input.focus()},clear:function(){this.$input.val(null)},escape:function(b){return a("<div>").text(b).html()},autosubmit:function(){},destroy:function(){},setClass:function(){this.options.inputclass&&this.$input.addClass(this.options.inputclass)},setAttr:function(a){void 0!==this.options[a]&&null!==this.options[a]&&this.$input.attr(a,this.options[a])},option:function(a,b){this.options[a]=b}},b.defaults={tpl:"",inputclass:null,escape:!0,scope:null,showbuttons:!0},a.extend(a.fn.editabletypes,{abstractinput:b})}(window.jQuery),function(a){"use strict";var b=function(){};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){var b=a.Deferred();return this.error=null,this.onSourceReady(function(){this.renderList(),b.resolve()},function(){this.error=this.options.sourceError,b.resolve()}),b.promise()},html2value:function(){return null},value2html:function(b,c,d,e){var f=a.Deferred(),g=function(){"function"==typeof d?d.call(c,b,this.sourceData,e):this.value2htmlFinal(b,c),f.resolve()};return null===b?g.call(this):this.onSourceReady(g,function(){f.resolve()}),f.promise()},onSourceReady:function(b,c){var d;if(a.isFunction(this.options.source)?(d=this.options.source.call(this.options.scope),this.sourceData=null):d=this.options.source,this.options.sourceCache&&a.isArray(this.sourceData))return b.call(this),void 0;try{d=a.fn.editableutils.tryParseJson(d,!1)}catch(e){return c.call(this),void 0}if("string"==typeof d){if(this.options.sourceCache){var f,g=d;if(a(document).data(g)||a(document).data(g,{}),f=a(document).data(g),f.loading===!1&&f.sourceData)return this.sourceData=f.sourceData,this.doPrepend(),b.call(this),void 0;if(f.loading===!0)return f.callbacks.push(a.proxy(function(){this.sourceData=f.sourceData,this.doPrepend(),b.call(this)},this)),f.err_callbacks.push(a.proxy(c,this)),void 0;f.loading=!0,f.callbacks=[],f.err_callbacks=[]}var h=a.extend({url:d,type:"get",cache:!1,dataType:"json",success:a.proxy(function(d){f&&(f.loading=!1),this.sourceData=this.makeArray(d),a.isArray(this.sourceData)?(f&&(f.sourceData=this.sourceData,a.each(f.callbacks,function(){this.call()})),this.doPrepend(),b.call(this)):(c.call(this),f&&a.each(f.err_callbacks,function(){this.call()}))},this),error:a.proxy(function(){c.call(this),f&&(f.loading=!1,a.each(f.err_callbacks,function(){this.call()}))},this)},this.options.sourceOptions);a.ajax(h)}else this.sourceData=this.makeArray(d),a.isArray(this.sourceData)?(this.doPrepend(),b.call(this)):c.call(this)},doPrepend:function(){null!==this.options.prepend&&void 0!==this.options.prepend&&(a.isArray(this.prependData)||(a.isFunction(this.options.prepend)&&(this.options.prepend=this.options.prepend.call(this.options.scope)),this.options.prepend=a.fn.editableutils.tryParseJson(this.options.prepend,!0),"string"==typeof this.options.prepend&&(this.options.prepend={"":this.options.prepend}),this.prependData=this.makeArray(this.options.prepend)),a.isArray(this.prependData)&&a.isArray(this.sourceData)&&(this.sourceData=this.prependData.concat(this.sourceData)))},renderList:function(){},value2htmlFinal:function(){},makeArray:function(b){var c,d,e,f,g=[];if(!b||"string"==typeof b)return null;if(a.isArray(b)){f=function(a,b){return d={value:a,text:b},c++>=2?!1:void 0};for(var h=0;h<b.length;h++)e=b[h],"object"==typeof e?(c=0,a.each(e,f),1===c?g.push(d):c>1&&(e.children&&(e.children=this.makeArray(e.children)),g.push(e))):g.push({value:e,text:e})}else a.each(b,function(a,b){g.push({value:a,text:b})});return g},option:function(a,b){this.options[a]=b,"source"===a&&(this.sourceData=null),"prepend"===a&&(this.prependData=null)}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{source:null,prepend:!1,sourceError:"Error when loading list",sourceCache:!0,sourceOptions:null}),a.fn.editabletypes.list=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("text",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.renderClear(),this.setClass(),this.setAttr("placeholder")},activate:function(){this.$input.is(":visible")&&(this.$input.focus(),a.fn.editableutils.setCursorPosition(this.$input.get(0),this.$input.val().length),this.toggleClear&&this.toggleClear())},renderClear:function(){this.options.clear&&(this.$clear=a('<span class="editable-clear-x"></span>'),this.$input.after(this.$clear).css("padding-right",24).keyup(a.proxy(function(b){if(!~a.inArray(b.keyCode,[40,38,9,13,27])){clearTimeout(this.t);var c=this;this.t=setTimeout(function(){c.toggleClear(b)},100)}},this)).parent().css("position","relative"),this.$clear.click(a.proxy(this.clear,this)))},postrender:function(){},toggleClear:function(){if(this.$clear){var a=this.$input.val().length,b=this.$clear.is(":visible");a&&!b&&this.$clear.show(),!a&&b&&this.$clear.hide()}},clear:function(){this.$clear.hide(),this.$input.val("").focus()}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<input type="text">',placeholder:null,clear:!0}),a.fn.editabletypes.text=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("textarea",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.setClass(),this.setAttr("placeholder"),this.setAttr("rows"),this.$input.keydown(function(b){b.ctrlKey&&13===b.which&&a(this).closest("form").submit()})},activate:function(){a.fn.editabletypes.text.prototype.activate.call(this)}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:"<textarea></textarea>",inputclass:"input-large",placeholder:null,rows:7}),a.fn.editabletypes.textarea=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("select",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.list),a.extend(b.prototype,{renderList:function(){this.$input.empty();var b=function(c,d){var e;if(a.isArray(d))for(var f=0;f<d.length;f++)e={},d[f].children?(e.label=d[f].text,c.append(b(a("<optgroup>",e),d[f].children))):(e.value=d[f].value,d[f].disabled&&(e.disabled=!0),c.append(a("<option>",e).text(d[f].text)));return c};b(this.$input,this.sourceData),this.setClass(),this.$input.on("keydown.editable",function(b){13===b.which&&a(this).closest("form").submit()})},value2htmlFinal:function(b,c){var d="",e=a.fn.editableutils.itemsByValue(b,this.sourceData);e.length&&(d=e[0].text),a.fn.editabletypes.abstractinput.prototype.value2html.call(this,d,c)},autosubmit:function(){this.$input.off("keydown.editable").on("change.editable",function(){a(this).closest("form").submit()})}}),b.defaults=a.extend({},a.fn.editabletypes.list.defaults,{tpl:"<select></select>"}),a.fn.editabletypes.select=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("checklist",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.list),a.extend(b.prototype,{renderList:function(){var b;if(this.$tpl.empty(),a.isArray(this.sourceData)){for(var c=0;c<this.sourceData.length;c++)b=a("<label>").append(a("<input>",{type:"checkbox",value:this.sourceData[c].value})).append(a("<span>").text(" "+this.sourceData[c].text)),a("<div>").append(b).appendTo(this.$tpl);this.$input=this.$tpl.find('input[type="checkbox"]'),this.setClass()}},value2str:function(b){return a.isArray(b)?b.sort().join(a.trim(this.options.separator)):""},str2value:function(b){var c,d=null;return"string"==typeof b&&b.length?(c=new RegExp("\\s*"+a.trim(this.options.separator)+"\\s*"),d=b.split(c)):d=a.isArray(b)?b:[b],d},value2input:function(b){this.$input.prop("checked",!1),a.isArray(b)&&b.length&&this.$input.each(function(c,d){var e=a(d);a.each(b,function(a,b){e.val()==b&&e.prop("checked",!0)})})},input2value:function(){var b=[];return this.$input.filter(":checked").each(function(c,d){b.push(a(d).val())}),b},value2htmlFinal:function(b,c){var d=[],e=a.fn.editableutils.itemsByValue(b,this.sourceData),f=this.options.escape;e.length?(a.each(e,function(b,c){var e=f?a.fn.editableutils.escape(c.text):c.text;d.push(e)}),a(c).html(d.join("<br>"))):a(c).empty()},activate:function(){this.$input.first().focus()},autosubmit:function(){this.$input.on("keydown",function(b){13===b.which&&a(this).closest("form").submit()})}}),b.defaults=a.extend({},a.fn.editabletypes.list.defaults,{tpl:'<div class="editable-checklist"></div>',inputclass:null,separator:","}),a.fn.editabletypes.checklist=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("password",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),a.extend(b.prototype,{value2html:function(b,c){b?a(c).text("[hidden]"):a(c).empty()},html2value:function(){return null}}),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="password">'}),a.fn.editabletypes.password=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("email",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="email">'}),a.fn.editabletypes.email=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("url",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="url">'}),a.fn.editabletypes.url=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("tel",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="tel">'}),a.fn.editabletypes.tel=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("number",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),a.extend(b.prototype,{render:function(){b.superclass.render.call(this),this.setAttr("min"),this.setAttr("max"),this.setAttr("step")},postrender:function(){this.$clear&&this.$clear.css({right:24})}}),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="number">',inputclass:"input-mini",min:null,max:null,step:null}),a.fn.editabletypes.number=b}(window.jQuery),function(a){"use strict";
var b=function(a){this.init("range",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.number),a.extend(b.prototype,{render:function(){this.$input=this.$tpl.filter("input"),this.setClass(),this.setAttr("min"),this.setAttr("max"),this.setAttr("step"),this.$input.on("input",function(){a(this).siblings("output").text(a(this).val())})},activate:function(){this.$input.focus()}}),b.defaults=a.extend({},a.fn.editabletypes.number.defaults,{tpl:'<input type="range"><output style="width: 30px; display: inline-block"></output>',inputclass:"input-medium"}),a.fn.editabletypes.range=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("time",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.setClass()}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<input type="time">'}),a.fn.editabletypes.time=b}(window.jQuery),function(a){"use strict";var b=function(c){if(this.init("select2",c,b.defaults),c.select2=c.select2||{},this.sourceData=null,c.placeholder&&(c.select2.placeholder=c.placeholder),!c.select2.tags&&c.source){var d=c.source;a.isFunction(c.source)&&(d=c.source.call(c.scope)),"string"==typeof d?(c.select2.ajax=c.select2.ajax||{},c.select2.ajax.data||(c.select2.ajax.data=function(a){return{query:a}}),c.select2.ajax.results||(c.select2.ajax.results=function(a){return{results:a}}),c.select2.ajax.url=d):(this.sourceData=this.convertSource(d),c.select2.data=this.sourceData)}if(this.options.select2=a.extend({},b.defaults.select2,c.select2),this.isMultiple=this.options.select2.tags||this.options.select2.multiple,this.isRemote="ajax"in this.options.select2,this.idFunc=this.options.select2.id,"function"!=typeof this.idFunc){var e=this.idFunc||"id";this.idFunc=function(a){return a[e]}}this.formatSelection=this.options.select2.formatSelection,"function"!=typeof this.formatSelection&&(this.formatSelection=function(a){return a.text})};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.setClass(),this.isRemote&&this.$input.on("select2-loaded",a.proxy(function(a){this.sourceData=a.items.results},this)),this.isMultiple&&this.$input.on("change",function(){a(this).closest("form").parent().triggerHandler("resize")})},value2html:function(c,d){var e,f="",g=this;this.options.select2.tags?e=c:this.sourceData&&(e=a.fn.editableutils.itemsByValue(c,this.sourceData,this.idFunc)),a.isArray(e)?(f=[],a.each(e,function(a,b){f.push(b&&"object"==typeof b?g.formatSelection(b):b)})):e&&(f=g.formatSelection(e)),f=a.isArray(f)?f.join(this.options.viewseparator):f,b.superclass.value2html.call(this,f,d)},html2value:function(a){return this.options.select2.tags?this.str2value(a,this.options.viewseparator):null},value2input:function(b){if(a.isArray(b)&&(b=b.join(this.getSeparator())),this.$input.data("select2")?this.$input.val(b).trigger("change",!0):(this.$input.val(b),this.$input.select2(this.options.select2)),this.isRemote&&!this.isMultiple&&!this.options.select2.initSelection){var c=this.options.select2.id,d=this.options.select2.formatSelection;if(!c&&!d){var e=a(this.options.scope);if(!e.data("editable").isEmpty){var f={id:b,text:e.text()};this.$input.select2("data",f)}}}},input2value:function(){return this.$input.select2("val")},str2value:function(b,c){if("string"!=typeof b||!this.isMultiple)return b;c=c||this.getSeparator();var d,e,f;if(null===b||b.length<1)return null;for(d=b.split(c),e=0,f=d.length;f>e;e+=1)d[e]=a.trim(d[e]);return d},autosubmit:function(){this.$input.on("change",function(b,c){c||a(this).closest("form").submit()})},getSeparator:function(){return this.options.select2.separator||a.fn.select2.defaults.separator},convertSource:function(b){if(a.isArray(b)&&b.length&&void 0!==b[0].value)for(var c=0;c<b.length;c++)void 0!==b[c].value&&(b[c].id=b[c].value,delete b[c].value);return b},destroy:function(){this.$input.data("select2")&&this.$input.select2("destroy")}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<input type="hidden">',select2:null,placeholder:null,source:null,viewseparator:", "}),a.fn.editabletypes.select2=b}(window.jQuery),function(a){var b=function(b,c){return this.$element=a(b),this.$element.is("input")?(this.options=a.extend({},a.fn.combodate.defaults,c,this.$element.data()),this.init(),void 0):(a.error("Combodate should be applied to INPUT element"),void 0)};b.prototype={constructor:b,init:function(){this.map={day:["D","date"],month:["M","month"],year:["Y","year"],hour:["[Hh]","hours"],minute:["m","minutes"],second:["s","seconds"],ampm:["[Aa]",""]},this.$widget=a('<span class="combodate"></span>').html(this.getTemplate()),this.initCombos(),this.$widget.on("change","select",a.proxy(function(b){this.$element.val(this.getValue()).change(),this.options.smartDays&&(a(b.target).is(".month")||a(b.target).is(".year"))&&this.fillCombo("day")},this)),this.$widget.find("select").css("width","auto"),this.$element.hide().after(this.$widget),this.setValue(this.$element.val()||this.options.value)},getTemplate:function(){var b=this.options.template;return a.each(this.map,function(a,c){c=c[0];var d=new RegExp(c+"+"),e=c.length>1?c.substring(1,2):c;b=b.replace(d,"{"+e+"}")}),b=b.replace(/ /g,"&nbsp;"),a.each(this.map,function(a,c){c=c[0];var d=c.length>1?c.substring(1,2):c;b=b.replace("{"+d+"}",'<select class="'+a+'"></select>')}),b},initCombos:function(){for(var a in this.map){var b=this.$widget.find("."+a);this["$"+a]=b.length?b:null,this.fillCombo(a)}},fillCombo:function(a){var b=this["$"+a];if(b){var c="fill"+a.charAt(0).toUpperCase()+a.slice(1),d=this[c](),e=b.val();b.empty();for(var f=0;f<d.length;f++)b.append('<option value="'+d[f][0]+'">'+d[f][1]+"</option>");b.val(e)}},fillCommon:function(a){var b,c=[];if("name"===this.options.firstItem){b=moment.relativeTime||moment.langData()._relativeTime;var d="function"==typeof b[a]?b[a](1,!0,a,!1):b[a];d=d.split(" ").reverse()[0],c.push(["",d])}else"empty"===this.options.firstItem&&c.push(["",""]);return c},fillDay:function(){var a,b,c=this.fillCommon("d"),d=-1!==this.options.template.indexOf("DD"),e=31;if(this.options.smartDays&&this.$month&&this.$year){var f=parseInt(this.$month.val(),10),g=parseInt(this.$year.val(),10);isNaN(f)||isNaN(g)||(e=moment([g,f]).daysInMonth())}for(b=1;e>=b;b++)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillMonth:function(){var a,b,c=this.fillCommon("M"),d=-1!==this.options.template.indexOf("MMMM"),e=-1!==this.options.template.indexOf("MMM"),f=-1!==this.options.template.indexOf("MM");for(b=0;11>=b;b++)a=d?moment().date(1).month(b).format("MMMM"):e?moment().date(1).month(b).format("MMM"):f?this.leadZero(b+1):b+1,c.push([b,a]);return c},fillYear:function(){var a,b,c=[],d=-1!==this.options.template.indexOf("YYYY");for(b=this.options.maxYear;b>=this.options.minYear;b--)a=d?b:(b+"").substring(2),c[this.options.yearDescending?"push":"unshift"]([b,a]);return c=this.fillCommon("y").concat(c)},fillHour:function(){var a,b,c=this.fillCommon("h"),d=-1!==this.options.template.indexOf("h"),e=(-1!==this.options.template.indexOf("H"),-1!==this.options.template.toLowerCase().indexOf("hh")),f=d?1:0,g=d?12:23;for(b=f;g>=b;b++)a=e?this.leadZero(b):b,c.push([b,a]);return c},fillMinute:function(){var a,b,c=this.fillCommon("m"),d=-1!==this.options.template.indexOf("mm");for(b=0;59>=b;b+=this.options.minuteStep)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillSecond:function(){var a,b,c=this.fillCommon("s"),d=-1!==this.options.template.indexOf("ss");for(b=0;59>=b;b+=this.options.secondStep)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillAmpm:function(){var a=-1!==this.options.template.indexOf("a"),b=(-1!==this.options.template.indexOf("A"),[["am",a?"am":"AM"],["pm",a?"pm":"PM"]]);return b},getValue:function(b){var c,d={},e=this,f=!1;return a.each(this.map,function(a){if("ampm"!==a){var b="day"===a?1:0;return d[a]=e["$"+a]?parseInt(e["$"+a].val(),10):b,isNaN(d[a])?(f=!0,!1):void 0}}),f?"":(this.$ampm&&(d.hour=12===d.hour?"am"===this.$ampm.val()?0:12:"am"===this.$ampm.val()?d.hour:d.hour+12),c=moment([d.year,d.month,d.day,d.hour,d.minute,d.second]),this.highlight(c),b=void 0===b?this.options.format:b,null===b?c.isValid()?c:null:c.isValid()?c.format(b):"")},setValue:function(b){function c(b,c){var d={};return b.children("option").each(function(b,e){var f,g=a(e).attr("value");""!==g&&(f=Math.abs(g-c),("undefined"==typeof d.distance||f<d.distance)&&(d={value:g,distance:f}))}),d.value}if(b){var d="string"==typeof b?moment(b,this.options.format):moment(b),e=this,f={};d.isValid()&&(a.each(this.map,function(a,b){"ampm"!==a&&(f[a]=d[b[1]]())}),this.$ampm&&(f.hour>=12?(f.ampm="pm",f.hour>12&&(f.hour-=12)):(f.ampm="am",0===f.hour&&(f.hour=12))),a.each(f,function(a,b){e["$"+a]&&("minute"===a&&e.options.minuteStep>1&&e.options.roundTime&&(b=c(e["$"+a],b)),"second"===a&&e.options.secondStep>1&&e.options.roundTime&&(b=c(e["$"+a],b)),e["$"+a].val(b))}),this.options.smartDays&&this.fillCombo("day"),this.$element.val(d.format(this.options.format)).change())}},highlight:function(a){a.isValid()?this.options.errorClass?this.$widget.removeClass(this.options.errorClass):this.$widget.find("select").css("border-color",this.borderColor):this.options.errorClass?this.$widget.addClass(this.options.errorClass):(this.borderColor||(this.borderColor=this.$widget.find("select").css("border-color")),this.$widget.find("select").css("border-color","red"))},leadZero:function(a){return 9>=a?"0"+a:a},destroy:function(){this.$widget.remove(),this.$element.removeData("combodate").show()}},a.fn.combodate=function(c){var d,e=Array.apply(null,arguments);return e.shift(),"getValue"===c&&this.length&&(d=this.eq(0).data("combodate"))?d.getValue.apply(d,e):this.each(function(){var d=a(this),f=d.data("combodate"),g="object"==typeof c&&c;f||d.data("combodate",f=new b(this,g)),"string"==typeof c&&"function"==typeof f[c]&&f[c].apply(f,e)})},a.fn.combodate.defaults={format:"DD-MM-YYYY HH:mm",template:"D / MMM / YYYY   H : mm",value:null,minYear:1970,maxYear:2015,yearDescending:!0,minuteStep:5,secondStep:1,firstItem:"empty",errorClass:null,roundTime:!0,smartDays:!1}}(window.jQuery),function(a){"use strict";var b=function(c){this.init("combodate",c,b.defaults),this.options.viewformat||(this.options.viewformat=this.options.format),c.combodate=a.fn.editableutils.tryParseJson(c.combodate,!0),this.options.combodate=a.extend({},b.defaults.combodate,c.combodate,{format:this.options.format,template:this.options.template})};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.$input.combodate(this.options.combodate),"bs3"===a.fn.editableform.engine&&this.$input.siblings().find("select").addClass("form-control"),this.options.inputclass&&this.$input.siblings().find("select").addClass(this.options.inputclass)},value2html:function(a,c){var d=a?a.format(this.options.viewformat):"";b.superclass.value2html.call(this,d,c)},html2value:function(a){return a?moment(a,this.options.viewformat):null},value2str:function(a){return a?a.format(this.options.format):""},str2value:function(a){return a?moment(a,this.options.format):null},value2submit:function(a){return this.value2str(a)},value2input:function(a){this.$input.combodate("setValue",a)},input2value:function(){return this.$input.combodate("getValue",null)},activate:function(){this.$input.siblings(".combodate").find("select").eq(0).focus()},autosubmit:function(){}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<input type="text">',inputclass:null,format:"YYYY-MM-DD",viewformat:null,template:"D / MMM / YYYY",combodate:null}),a.fn.editabletypes.combodate=b}(window.jQuery),function(a){"use strict";var b=a.fn.editableform.Constructor.prototype.initInput;a.extend(a.fn.editableform.Constructor.prototype,{initTemplate:function(){this.$form=a(a.fn.editableform.template),this.$form.find(".control-group").addClass("form-group"),this.$form.find(".editable-error-block").addClass("help-block")},initInput:function(){b.apply(this);var c=null===this.input.options.inputclass||this.input.options.inputclass===!1,d="input-sm",e="text,select,textarea,password,email,url,tel,number,range,time,typeaheadjs".split(",");~a.inArray(this.input.type,e)&&(this.input.$input.addClass("form-control"),c&&(this.input.options.inputclass=d,this.input.$input.addClass(d)));for(var f=this.$form.find(".editable-buttons"),g=c?[d]:this.input.options.inputclass.split(" "),h=0;h<g.length;h++)"input-lg"===g[h].toLowerCase()&&f.find("button").removeClass("btn-sm").addClass("btn-lg")}}),a.fn.editableform.buttons='<button type="submit" class="btn btn-primary btn-sm editable-submit"><i class="fas fa-check"></i></button><button type="button" class="btn btn-default btn-sm editable-cancel"><i class="fas fa-times"></i></button>',a.fn.editableform.errorGroupClass="has-error",a.fn.editableform.errorBlockClass=null,a.fn.editableform.engine="bs3"}(window.jQuery),function(a){"use strict";a.extend(a.fn.editableContainer.Popup.prototype,{containerName:"popover",containerDataName:"bs.popover",innerCss:".popover-content",defaults:a.fn.popover.Constructor.DEFAULTS,initContainer:function(){a.extend(this.containerOptions,{trigger:"manual",selector:!1,content:" ",template:this.defaults.template});var b;this.$element.data("template")&&(b=this.$element.data("template"),this.$element.removeData("template")),this.call(this.containerOptions),b&&this.$element.data("template",b)},innerShow:function(){this.call("show")},innerHide:function(){this.call("hide")},innerDestroy:function(){this.call("destroy")},setContainerOption:function(a,b){this.container().options[a]=b},setPosition:function(){!function(){var a=this.tip(),b="function"==typeof this.options.placement?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,c=/\s?auto?\s?/i,d=c.test(b);d&&(b=b.replace(c,"")||"top");var e=this.getPosition(),f=a[0].offsetWidth,g=a[0].offsetHeight;if(d){var h=this.$element.parent(),i=b,j=document.documentElement.scrollTop||document.body.scrollTop,k="body"==this.options.container?window.innerWidth:h.outerWidth(),l="body"==this.options.container?window.innerHeight:h.outerHeight(),m="body"==this.options.container?0:h.offset().left;b="bottom"==b&&e.top+e.height+g-j>l?"top":"top"==b&&e.top-j-g<0?"bottom":"right"==b&&e.right+f>k?"left":"left"==b&&e.left-f<m?"right":b,a.removeClass(i).addClass(b)}var n=this.getCalculatedOffset(b,e,f,g);this.applyPlacement(n,b)}.call(this.container())}})}(window.jQuery),function(a){function b(){return new Date(Date.UTC.apply(Date,arguments))}function c(b,c){var d,e=a(b).data(),f={},g=new RegExp("^"+c.toLowerCase()+"([A-Z])"),c=new RegExp("^"+c.toLowerCase());for(var h in e)c.test(h)&&(d=h.replace(g,function(a,b){return b.toLowerCase()}),f[d]=e[h]);return f}function d(b){var c={};if(k[b]||(b=b.split("-")[0],k[b])){var d=k[b];return a.each(j,function(a,b){b in d&&(c[b]=d[b])}),c}}var e=function(b,c){this._process_options(c),this.element=a(b),this.isInline=!1,this.isInput=this.element.is("input"),this.component=this.element.is(".date")?this.element.find(".add-on, .btn"):!1,this.hasInput=this.component&&this.element.find("input").length,this.component&&0===this.component.length&&(this.component=!1),this.picker=a(l.template),this._buildEvents(),this._attachEvents(),this.isInline?this.picker.addClass("datepicker-inline").appendTo(this.element):this.picker.addClass("datepicker-dropdown dropdown-menu"),this.o.rtl&&(this.picker.addClass("datepicker-rtl"),this.picker.find(".prev i, .next i").toggleClass("icon-arrow-left icon-arrow-right")),this.viewMode=this.o.startView,this.o.calendarWeeks&&this.picker.find("tfoot th.today").attr("colspan",function(a,b){return parseInt(b)+1}),this._allow_update=!1,this.setStartDate(this.o.startDate),this.setEndDate(this.o.endDate),this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled),this.fillDow(),this.fillMonths(),this._allow_update=!0,this.update(),this.showMode(),this.isInline&&this.show()};e.prototype={constructor:e,_process_options:function(b){this._o=a.extend({},this._o,b);var c=this.o=a.extend({},this._o),d=c.language;switch(k[d]||(d=d.split("-")[0],k[d]||(d=i.language)),c.language=d,c.startView){case 2:case"decade":c.startView=2;break;case 1:case"year":c.startView=1;break;default:c.startView=0}switch(c.minViewMode){case 1:case"months":c.minViewMode=1;break;case 2:case"years":c.minViewMode=2;break;default:c.minViewMode=0}c.startView=Math.max(c.startView,c.minViewMode),c.weekStart%=7,c.weekEnd=(c.weekStart+6)%7;var e=l.parseFormat(c.format);c.startDate!==-1/0&&(c.startDate=l.parseDate(c.startDate,e,c.language)),1/0!==c.endDate&&(c.endDate=l.parseDate(c.endDate,e,c.language)),c.daysOfWeekDisabled=c.daysOfWeekDisabled||[],a.isArray(c.daysOfWeekDisabled)||(c.daysOfWeekDisabled=c.daysOfWeekDisabled.split(/[,\s]*/)),c.daysOfWeekDisabled=a.map(c.daysOfWeekDisabled,function(a){return parseInt(a,10)})},_events:[],_secondaryEvents:[],_applyEvents:function(a){for(var b,c,d=0;d<a.length;d++)b=a[d][0],c=a[d][1],b.on(c)},_unapplyEvents:function(a){for(var b,c,d=0;d<a.length;d++)b=a[d][0],c=a[d][1],b.off(c)},_buildEvents:function(){this.isInput?this._events=[[this.element,{focus:a.proxy(this.show,this),keyup:a.proxy(this.update,this),keydown:a.proxy(this.keydown,this)}]]:this.component&&this.hasInput?this._events=[[this.element.find("input"),{focus:a.proxy(this.show,this),keyup:a.proxy(this.update,this),keydown:a.proxy(this.keydown,this)}],[this.component,{click:a.proxy(this.show,this)}]]:this.element.is("div")?this.isInline=!0:this._events=[[this.element,{click:a.proxy(this.show,this)}]],this._secondaryEvents=[[this.picker,{click:a.proxy(this.click,this)}],[a(window),{resize:a.proxy(this.place,this)}],[a(document),{mousedown:a.proxy(function(a){this.element.is(a.target)||this.element.find(a.target).size()||this.picker.is(a.target)||this.picker.find(a.target).size()||this.hide()},this)}]]},_attachEvents:function(){this._detachEvents(),this._applyEvents(this._events)},_detachEvents:function(){this._unapplyEvents(this._events)},_attachSecondaryEvents:function(){this._detachSecondaryEvents(),this._applyEvents(this._secondaryEvents)},_detachSecondaryEvents:function(){this._unapplyEvents(this._secondaryEvents)},_trigger:function(b,c){var d=c||this.date,e=new Date(d.getTime()+6e4*d.getTimezoneOffset());this.element.trigger({type:b,date:e,format:a.proxy(function(a){var b=a||this.o.format;return l.formatDate(d,b,this.o.language)},this)})},show:function(a){this.isInline||this.picker.appendTo("body"),this.picker.show(),this.height=this.component?this.component.outerHeight():this.element.outerHeight(),this.place(),this._attachSecondaryEvents(),a&&a.preventDefault(),this._trigger("show")},hide:function(){this.isInline||this.picker.is(":visible")&&(this.picker.hide().detach(),this._detachSecondaryEvents(),this.viewMode=this.o.startView,this.showMode(),this.o.forceParse&&(this.isInput&&this.element.val()||this.hasInput&&this.element.find("input").val())&&this.setValue(),this._trigger("hide"))},remove:function(){this.hide(),this._detachEvents(),this._detachSecondaryEvents(),this.picker.remove(),delete this.element.data().datepicker,this.isInput||delete this.element.data().date},getDate:function(){var a=this.getUTCDate();return new Date(a.getTime()+6e4*a.getTimezoneOffset())},getUTCDate:function(){return this.date},setDate:function(a){this.setUTCDate(new Date(a.getTime()-6e4*a.getTimezoneOffset()))},setUTCDate:function(a){this.date=a,this.setValue()},setValue:function(){var a=this.getFormattedDate();this.isInput?this.element.val(a):this.component&&this.element.find("input").val(a)},getFormattedDate:function(a){return void 0===a&&(a=this.o.format),l.formatDate(this.date,a,this.o.language)},setStartDate:function(a){this._process_options({startDate:a}),this.update(),this.updateNavArrows()},setEndDate:function(a){this._process_options({endDate:a}),this.update(),this.updateNavArrows()},setDaysOfWeekDisabled:function(a){this._process_options({daysOfWeekDisabled:a}),this.update(),this.updateNavArrows()},place:function(){if(!this.isInline){var b=parseInt(this.element.parents().filter(function(){return"auto"!=a(this).css("z-index")}).first().css("z-index"))+10,c=this.component?this.component.parent().offset():this.element.offset(),d=this.component?this.component.outerHeight(!0):this.element.outerHeight(!0);this.picker.css({top:c.top+d,left:c.left,zIndex:b})}},_allow_update:!0,update:function(){if(this._allow_update){var a,b=!1;arguments&&arguments.length&&("string"==typeof arguments[0]||arguments[0]instanceof Date)?(a=arguments[0],b=!0):(a=this.isInput?this.element.val():this.element.data("date")||this.element.find("input").val(),delete this.element.data().date),this.date=l.parseDate(a,this.o.format,this.o.language),b&&this.setValue(),this.viewDate=this.date<this.o.startDate?new Date(this.o.startDate):this.date>this.o.endDate?new Date(this.o.endDate):new Date(this.date),this.fill()}},fillDow:function(){var a=this.o.weekStart,b="<tr>";if(this.o.calendarWeeks){var c='<th class="cw">&nbsp;</th>';b+=c,this.picker.find(".datepicker-days thead tr:first-child").prepend(c)}for(;a<this.o.weekStart+7;)b+='<th class="dow">'+k[this.o.language].daysMin[a++%7]+"</th>";b+="</tr>",this.picker.find(".datepicker-days thead").append(b)},fillMonths:function(){for(var a="",b=0;12>b;)a+='<span class="month">'+k[this.o.language].monthsShort[b++]+"</span>";this.picker.find(".datepicker-months td").html(a)},setRange:function(b){b&&b.length?this.range=a.map(b,function(a){return a.valueOf()}):delete this.range,this.fill()},getClassNames:function(b){var c=[],d=this.viewDate.getUTCFullYear(),e=this.viewDate.getUTCMonth(),f=this.date.valueOf(),g=new Date;return b.getUTCFullYear()<d||b.getUTCFullYear()==d&&b.getUTCMonth()<e?c.push("old"):(b.getUTCFullYear()>d||b.getUTCFullYear()==d&&b.getUTCMonth()>e)&&c.push("new"),this.o.todayHighlight&&b.getUTCFullYear()==g.getFullYear()&&b.getUTCMonth()==g.getMonth()&&b.getUTCDate()==g.getDate()&&c.push("today"),f&&b.valueOf()==f&&c.push("active"),(b.valueOf()<this.o.startDate||b.valueOf()>this.o.endDate||-1!==a.inArray(b.getUTCDay(),this.o.daysOfWeekDisabled))&&c.push("disabled"),this.range&&(b>this.range[0]&&b<this.range[this.range.length-1]&&c.push("range"),-1!=a.inArray(b.valueOf(),this.range)&&c.push("selected")),c},fill:function(){var c,d=new Date(this.viewDate),e=d.getUTCFullYear(),f=d.getUTCMonth(),g=this.o.startDate!==-1/0?this.o.startDate.getUTCFullYear():-1/0,h=this.o.startDate!==-1/0?this.o.startDate.getUTCMonth():-1/0,i=1/0!==this.o.endDate?this.o.endDate.getUTCFullYear():1/0,j=1/0!==this.o.endDate?this.o.endDate.getUTCMonth():1/0;this.date&&this.date.valueOf(),this.picker.find(".datepicker-days thead th.datepicker-switch").text(k[this.o.language].months[f]+" "+e),this.picker.find("tfoot th.today").text(k[this.o.language].today).toggle(this.o.todayBtn!==!1),this.picker.find("tfoot th.clear").text(k[this.o.language].clear).toggle(this.o.clearBtn!==!1),this.updateNavArrows(),this.fillMonths();var m=b(e,f-1,28,0,0,0,0),n=l.getDaysInMonth(m.getUTCFullYear(),m.getUTCMonth());m.setUTCDate(n),m.setUTCDate(n-(m.getUTCDay()-this.o.weekStart+7)%7);var o=new Date(m);o.setUTCDate(o.getUTCDate()+42),o=o.valueOf();for(var p,q=[];m.valueOf()<o;){if(m.getUTCDay()==this.o.weekStart&&(q.push("<tr>"),this.o.calendarWeeks)){var r=new Date(+m+864e5*((this.o.weekStart-m.getUTCDay()-7)%7)),s=new Date(+r+864e5*((11-r.getUTCDay())%7)),t=new Date(+(t=b(s.getUTCFullYear(),0,1))+864e5*((11-t.getUTCDay())%7)),u=(s-t)/864e5/7+1;q.push('<td class="cw">'+u+"</td>")}p=this.getClassNames(m),p.push("day");var v=this.o.beforeShowDay(m);void 0===v?v={}:"boolean"==typeof v?v={enabled:v}:"string"==typeof v&&(v={classes:v}),v.enabled===!1&&p.push("disabled"),v.classes&&(p=p.concat(v.classes.split(/\s+/))),v.tooltip&&(c=v.tooltip),p=a.unique(p),q.push('<td class="'+p.join(" ")+'"'+(c?' title="'+c+'"':"")+">"+m.getUTCDate()+"</td>"),m.getUTCDay()==this.o.weekEnd&&q.push("</tr>"),m.setUTCDate(m.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").empty().append(q.join(""));var w=this.date&&this.date.getUTCFullYear(),x=this.picker.find(".datepicker-months").find("th:eq(1)").text(e).end().find("span").removeClass("active");w&&w==e&&x.eq(this.date.getUTCMonth()).addClass("active"),(g>e||e>i)&&x.addClass("disabled"),e==g&&x.slice(0,h).addClass("disabled"),e==i&&x.slice(j+1).addClass("disabled"),q="",e=10*parseInt(e/10,10);var y=this.picker.find(".datepicker-years").find("th:eq(1)").text(e+"-"+(e+9)).end().find("td");e-=1;for(var z=-1;11>z;z++)q+='<span class="year'+(-1==z?" old":10==z?" new":"")+(w==e?" active":"")+(g>e||e>i?" disabled":"")+'">'+e+"</span>",e+=1;y.html(q)},updateNavArrows:function(){if(this._allow_update){var a=new Date(this.viewDate),b=a.getUTCFullYear(),c=a.getUTCMonth();switch(this.viewMode){case 0:this.o.startDate!==-1/0&&b<=this.o.startDate.getUTCFullYear()&&c<=this.o.startDate.getUTCMonth()?this.picker.find(".prev").css({visibility:"hidden"}):this.picker.find(".prev").css({visibility:"visible"}),1/0!==this.o.endDate&&b>=this.o.endDate.getUTCFullYear()&&c>=this.o.endDate.getUTCMonth()?this.picker.find(".next").css({visibility:"hidden"}):this.picker.find(".next").css({visibility:"visible"});break;case 1:case 2:this.o.startDate!==-1/0&&b<=this.o.startDate.getUTCFullYear()?this.picker.find(".prev").css({visibility:"hidden"}):this.picker.find(".prev").css({visibility:"visible"}),1/0!==this.o.endDate&&b>=this.o.endDate.getUTCFullYear()?this.picker.find(".next").css({visibility:"hidden"}):this.picker.find(".next").css({visibility:"visible"})}}},click:function(c){c.preventDefault();var d=a(c.target).closest("span, td, th");if(1==d.length)switch(d[0].nodeName.toLowerCase()){case"th":switch(d[0].className){case"datepicker-switch":this.showMode(1);break;case"prev":case"next":var e=l.modes[this.viewMode].navStep*("prev"==d[0].className?-1:1);switch(this.viewMode){case 0:this.viewDate=this.moveMonth(this.viewDate,e);break;case 1:case 2:this.viewDate=this.moveYear(this.viewDate,e)}this.fill();break;case"today":var f=new Date;f=b(f.getFullYear(),f.getMonth(),f.getDate(),0,0,0),this.showMode(-2);var g="linked"==this.o.todayBtn?null:"view";this._setDate(f,g);break;case"clear":var h;this.isInput?h=this.element:this.component&&(h=this.element.find("input")),h&&h.val("").change(),this._trigger("changeDate"),this.update(),this.o.autoclose&&this.hide()}break;case"span":if(!d.is(".disabled")){if(this.viewDate.setUTCDate(1),d.is(".month")){var i=1,j=d.parent().find("span").index(d),k=this.viewDate.getUTCFullYear();this.viewDate.setUTCMonth(j),this._trigger("changeMonth",this.viewDate),1===this.o.minViewMode&&this._setDate(b(k,j,i,0,0,0,0))}else{var k=parseInt(d.text(),10)||0,i=1,j=0;this.viewDate.setUTCFullYear(k),this._trigger("changeYear",this.viewDate),2===this.o.minViewMode&&this._setDate(b(k,j,i,0,0,0,0))}this.showMode(-1),this.fill()}break;case"td":if(d.is(".day")&&!d.is(".disabled")){var i=parseInt(d.text(),10)||1,k=this.viewDate.getUTCFullYear(),j=this.viewDate.getUTCMonth();d.is(".old")?0===j?(j=11,k-=1):j-=1:d.is(".new")&&(11==j?(j=0,k+=1):j+=1),this._setDate(b(k,j,i,0,0,0,0))}}},_setDate:function(a,b){b&&"date"!=b||(this.date=new Date(a)),b&&"view"!=b||(this.viewDate=new Date(a)),this.fill(),this.setValue(),this._trigger("changeDate");var c;this.isInput?c=this.element:this.component&&(c=this.element.find("input")),c&&(c.change(),!this.o.autoclose||b&&"date"!=b||this.hide())},moveMonth:function(a,b){if(!b)return a;var c,d,e=new Date(a.valueOf()),f=e.getUTCDate(),g=e.getUTCMonth(),h=Math.abs(b);if(b=b>0?1:-1,1==h)d=-1==b?function(){return e.getUTCMonth()==g}:function(){return e.getUTCMonth()!=c},c=g+b,e.setUTCMonth(c),(0>c||c>11)&&(c=(c+12)%12);else{for(var i=0;h>i;i++)e=this.moveMonth(e,b);c=e.getUTCMonth(),e.setUTCDate(f),d=function(){return c!=e.getUTCMonth()}}for(;d();)e.setUTCDate(--f),e.setUTCMonth(c);return e},moveYear:function(a,b){return this.moveMonth(a,12*b)},dateWithinRange:function(a){return a>=this.o.startDate&&a<=this.o.endDate},keydown:function(a){if(this.picker.is(":not(:visible)"))return 27==a.keyCode&&this.show(),void 0;var b,c,d,e=!1;switch(a.keyCode){case 27:this.hide(),a.preventDefault();break;case 37:case 39:if(!this.o.keyboardNavigation)break;b=37==a.keyCode?-1:1,a.ctrlKey?(c=this.moveYear(this.date,b),d=this.moveYear(this.viewDate,b)):a.shiftKey?(c=this.moveMonth(this.date,b),d=this.moveMonth(this.viewDate,b)):(c=new Date(this.date),c.setUTCDate(this.date.getUTCDate()+b),d=new Date(this.viewDate),d.setUTCDate(this.viewDate.getUTCDate()+b)),this.dateWithinRange(c)&&(this.date=c,this.viewDate=d,this.setValue(),this.update(),a.preventDefault(),e=!0);break;case 38:case 40:if(!this.o.keyboardNavigation)break;b=38==a.keyCode?-1:1,a.ctrlKey?(c=this.moveYear(this.date,b),d=this.moveYear(this.viewDate,b)):a.shiftKey?(c=this.moveMonth(this.date,b),d=this.moveMonth(this.viewDate,b)):(c=new Date(this.date),c.setUTCDate(this.date.getUTCDate()+7*b),d=new Date(this.viewDate),d.setUTCDate(this.viewDate.getUTCDate()+7*b)),this.dateWithinRange(c)&&(this.date=c,this.viewDate=d,this.setValue(),this.update(),a.preventDefault(),e=!0);break;case 13:this.hide(),a.preventDefault();break;case 9:this.hide()}if(e){this._trigger("changeDate");var f;this.isInput?f=this.element:this.component&&(f=this.element.find("input")),f&&f.change()}},showMode:function(a){a&&(this.viewMode=Math.max(this.o.minViewMode,Math.min(2,this.viewMode+a))),this.picker.find(">div").hide().filter(".datepicker-"+l.modes[this.viewMode].clsName).css("display","block"),this.updateNavArrows()}};var f=function(b,c){this.element=a(b),this.inputs=a.map(c.inputs,function(a){return a.jquery?a[0]:a}),delete c.inputs,a(this.inputs).datepicker(c).bind("changeDate",a.proxy(this.dateUpdated,this)),this.pickers=a.map(this.inputs,function(b){return a(b).data("datepicker")}),this.updateDates()};f.prototype={updateDates:function(){this.dates=a.map(this.pickers,function(a){return a.date}),this.updateRanges()},updateRanges:function(){var b=a.map(this.dates,function(a){return a.valueOf()});a.each(this.pickers,function(a,c){c.setRange(b)})},dateUpdated:function(b){var c=a(b.target).data("datepicker"),d=c.getUTCDate(),e=a.inArray(b.target,this.inputs),f=this.inputs.length;if(-1!=e){if(d<this.dates[e])for(;e>=0&&d<this.dates[e];)this.pickers[e--].setUTCDate(d);else if(d>this.dates[e])for(;f>e&&d>this.dates[e];)this.pickers[e++].setUTCDate(d);this.updateDates()}},remove:function(){a.map(this.pickers,function(a){a.remove()}),delete this.element.data().datepicker}};var g=a.fn.datepicker,h=a.fn.datepicker=function(b){var g=Array.apply(null,arguments);g.shift();var h;return this.each(function(){var j=a(this),k=j.data("datepicker"),l="object"==typeof b&&b;if(!k){var m=c(this,"date"),n=a.extend({},i,m,l),o=d(n.language),p=a.extend({},i,o,m,l);if(j.is(".input-daterange")||p.inputs){var q={inputs:p.inputs||j.find("input").toArray()};j.data("datepicker",k=new f(this,a.extend(p,q)))}else j.data("datepicker",k=new e(this,p))}return"string"==typeof b&&"function"==typeof k[b]&&(h=k[b].apply(k,g),void 0!==h)?!1:void 0}),void 0!==h?h:this},i=a.fn.datepicker.defaults={autoclose:!1,beforeShowDay:a.noop,calendarWeeks:!1,clearBtn:!1,daysOfWeekDisabled:[],endDate:1/0,forceParse:!0,format:"mm/dd/yyyy",keyboardNavigation:!0,language:"en",minViewMode:0,rtl:!1,startDate:-1/0,startView:0,todayBtn:!1,todayHighlight:!1,weekStart:0},j=a.fn.datepicker.locale_opts=["format","rtl","weekStart"];a.fn.datepicker.Constructor=e;var k=a.fn.datepicker.dates={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa","Su"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear"}},l={modes:[{clsName:"days",navFnc:"Month",navStep:1},{clsName:"months",navFnc:"FullYear",navStep:1},{clsName:"years",navFnc:"FullYear",navStep:10}],isLeapYear:function(a){return 0===a%4&&0!==a%100||0===a%400
},getDaysInMonth:function(a,b){return[31,l.isLeapYear(a)?29:28,31,30,31,30,31,31,30,31,30,31][b]},validParts:/dd?|DD?|mm?|MM?|yy(?:yy)?/g,nonpunctuation:/[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,parseFormat:function(a){var b=a.replace(this.validParts,"\0").split("\0"),c=a.match(this.validParts);if(!b||!b.length||!c||0===c.length)throw new Error("Invalid date format.");return{separators:b,parts:c}},parseDate:function(c,d,f){if(c instanceof Date)return c;if("string"==typeof d&&(d=l.parseFormat(d)),/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(c)){var g,h,i=/([\-+]\d+)([dmwy])/,j=c.match(/([\-+]\d+)([dmwy])/g);c=new Date;for(var m=0;m<j.length;m++)switch(g=i.exec(j[m]),h=parseInt(g[1]),g[2]){case"d":c.setUTCDate(c.getUTCDate()+h);break;case"m":c=e.prototype.moveMonth.call(e.prototype,c,h);break;case"w":c.setUTCDate(c.getUTCDate()+7*h);break;case"y":c=e.prototype.moveYear.call(e.prototype,c,h)}return b(c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate(),0,0,0)}var n,o,g,j=c&&c.match(this.nonpunctuation)||[],c=new Date,p={},q=["yyyy","yy","M","MM","m","mm","d","dd"],r={yyyy:function(a,b){return a.setUTCFullYear(b)},yy:function(a,b){return a.setUTCFullYear(2e3+b)},m:function(a,b){for(b-=1;0>b;)b+=12;for(b%=12,a.setUTCMonth(b);a.getUTCMonth()!=b;)a.setUTCDate(a.getUTCDate()-1);return a},d:function(a,b){return a.setUTCDate(b)}};r.M=r.MM=r.mm=r.m,r.dd=r.d,c=b(c.getFullYear(),c.getMonth(),c.getDate(),0,0,0);var s=d.parts.slice();if(j.length!=s.length&&(s=a(s).filter(function(b,c){return-1!==a.inArray(c,q)}).toArray()),j.length==s.length){for(var m=0,t=s.length;t>m;m++){if(n=parseInt(j[m],10),g=s[m],isNaN(n))switch(g){case"MM":o=a(k[f].months).filter(function(){var a=this.slice(0,j[m].length),b=j[m].slice(0,a.length);return a==b}),n=a.inArray(o[0],k[f].months)+1;break;case"M":o=a(k[f].monthsShort).filter(function(){var a=this.slice(0,j[m].length),b=j[m].slice(0,a.length);return a==b}),n=a.inArray(o[0],k[f].monthsShort)+1}p[g]=n}for(var u,m=0;m<q.length;m++)u=q[m],u in p&&!isNaN(p[u])&&r[u](c,p[u])}return c},formatDate:function(b,c,d){"string"==typeof c&&(c=l.parseFormat(c));var e={d:b.getUTCDate(),D:k[d].daysShort[b.getUTCDay()],DD:k[d].days[b.getUTCDay()],m:b.getUTCMonth()+1,M:k[d].monthsShort[b.getUTCMonth()],MM:k[d].months[b.getUTCMonth()],yy:b.getUTCFullYear().toString().substring(2),yyyy:b.getUTCFullYear()};e.dd=(e.d<10?"0":"")+e.d,e.mm=(e.m<10?"0":"")+e.m;for(var b=[],f=a.extend([],c.separators),g=0,h=c.parts.length;h>=g;g++)f.length&&b.push(f.shift()),b.push(e[c.parts[g]]);return b.join("")},headTemplate:'<thead><tr><th class="prev"><i class="icon-arrow-left"/></th><th colspan="5" class="datepicker-switch"></th><th class="next"><i class="icon-arrow-right"/></th></tr></thead>',contTemplate:'<tbody><tr><td colspan="7"></td></tr></tbody>',footTemplate:'<tfoot><tr><th colspan="7" class="today"></th></tr><tr><th colspan="7" class="clear"></th></tr></tfoot>'};l.template='<div class="datepicker"><div class="datepicker-days"><table class=" table-condensed">'+l.headTemplate+"<tbody></tbody>"+l.footTemplate+"</table>"+"</div>"+'<div class="datepicker-months">'+'<table class="table-condensed">'+l.headTemplate+l.contTemplate+l.footTemplate+"</table>"+"</div>"+'<div class="datepicker-years">'+'<table class="table-condensed">'+l.headTemplate+l.contTemplate+l.footTemplate+"</table>"+"</div>"+"</div>",a.fn.datepicker.DPGlobal=l,a.fn.datepicker.noConflict=function(){return a.fn.datepicker=g,this},a(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(b){var c=a(this);c.data("datepicker")||(b.preventDefault(),h.call(c,"show"))}),a(function(){h.call(a('[data-provide="datepicker-inline"]'))})}(window.jQuery),function(a){"use strict";a.fn.bdatepicker=a.fn.datepicker.noConflict(),a.fn.datepicker||(a.fn.datepicker=a.fn.bdatepicker);var b=function(a){this.init("date",a,b.defaults),this.initPicker(a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{initPicker:function(b,c){this.options.viewformat||(this.options.viewformat=this.options.format),b.datepicker=a.fn.editableutils.tryParseJson(b.datepicker,!0),this.options.datepicker=a.extend({},c.datepicker,b.datepicker,{format:this.options.viewformat}),this.options.datepicker.language=this.options.datepicker.language||"en",this.dpg=a.fn.bdatepicker.DPGlobal,this.parsedFormat=this.dpg.parseFormat(this.options.format),this.parsedViewFormat=this.dpg.parseFormat(this.options.viewformat)},render:function(){this.$input.bdatepicker(this.options.datepicker),this.options.clear&&(this.$clear=a('<a href="#"></a>').html(this.options.clear).click(a.proxy(function(a){a.preventDefault(),a.stopPropagation(),this.clear()},this)),this.$tpl.parent().append(a('<div class="editable-clear">').append(this.$clear)))},value2html:function(a,c){var d=a?this.dpg.formatDate(a,this.parsedViewFormat,this.options.datepicker.language):"";b.superclass.value2html.call(this,d,c)},html2value:function(a){return this.parseDate(a,this.parsedViewFormat)},value2str:function(a){return a?this.dpg.formatDate(a,this.parsedFormat,this.options.datepicker.language):""},str2value:function(a){return this.parseDate(a,this.parsedFormat)},value2submit:function(a){return this.value2str(a)},value2input:function(a){this.$input.bdatepicker("update",a)},input2value:function(){return this.$input.data("datepicker").date},activate:function(){},clear:function(){this.$input.data("datepicker").date=null,this.$input.find(".active").removeClass("active"),this.options.showbuttons||this.$input.closest("form").submit()},autosubmit:function(){this.$input.on("mouseup",".day",function(b){if(!a(b.currentTarget).is(".old")&&!a(b.currentTarget).is(".new")){var c=a(this).closest("form");setTimeout(function(){c.submit()},200)}})},parseDate:function(a,b){var c,d=null;return a&&(d=this.dpg.parseDate(a,b,this.options.datepicker.language),"string"==typeof a&&(c=this.dpg.formatDate(d,b,this.options.datepicker.language),a!==c&&(d=null))),d}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<div class="editable-date well"></div>',inputclass:null,format:"yyyy-mm-dd",viewformat:null,datepicker:{weekStart:0,startView:0,minViewMode:0,autoclose:!1},clear:"&times; clear"}),a.fn.editabletypes.date=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("datefield",a,b.defaults),this.initPicker(a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.date),a.extend(b.prototype,{render:function(){this.$input=this.$tpl.find("input"),this.setClass(),this.setAttr("placeholder"),this.$tpl.bdatepicker(this.options.datepicker),this.$input.off("focus keydown"),this.$input.keyup(a.proxy(function(){this.$tpl.removeData("date"),this.$tpl.bdatepicker("update")},this))},value2input:function(a){this.$input.val(a?this.dpg.formatDate(a,this.parsedViewFormat,this.options.datepicker.language):""),this.$tpl.bdatepicker("update")},input2value:function(){return this.html2value(this.$input.val())},activate:function(){a.fn.editabletypes.text.prototype.activate.call(this)},autosubmit:function(){}}),b.defaults=a.extend({},a.fn.editabletypes.date.defaults,{tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',inputclass:"input-small",datepicker:{weekStart:0,startView:0,minViewMode:0,autoclose:!0}}),a.fn.editabletypes.datefield=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("datetime",a,b.defaults),this.initPicker(a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{initPicker:function(b,c){this.options.viewformat||(this.options.viewformat=this.options.format),b.datetimepicker=a.fn.editableutils.tryParseJson(b.datetimepicker,!0),this.options.datetimepicker=a.extend({},c.datetimepicker,b.datetimepicker,{format:this.options.viewformat}),this.options.datetimepicker.language=this.options.datetimepicker.language||"en",this.dpg=a.fn.datetimepicker.DPGlobal,this.parsedFormat=this.dpg.parseFormat(this.options.format,this.options.formatType),this.parsedViewFormat=this.dpg.parseFormat(this.options.viewformat,this.options.formatType)},render:function(){this.$input.datetimepicker(this.options.datetimepicker),this.$input.on("changeMode",function(){var b=a(this).closest("form").parent();setTimeout(function(){b.triggerHandler("resize")},0)}),this.options.clear&&(this.$clear=a('<a href="#"></a>').html(this.options.clear).click(a.proxy(function(a){a.preventDefault(),a.stopPropagation(),this.clear()},this)),this.$tpl.parent().append(a('<div class="editable-clear">').append(this.$clear)))},value2html:function(a,c){var d=a?this.dpg.formatDate(this.toUTC(a),this.parsedViewFormat,this.options.datetimepicker.language,this.options.formatType):"";return c?(b.superclass.value2html.call(this,d,c),void 0):d},html2value:function(a){var b=this.parseDate(a,this.parsedViewFormat);return b?this.fromUTC(b):null},value2str:function(a){return a?this.dpg.formatDate(this.toUTC(a),this.parsedFormat,this.options.datetimepicker.language,this.options.formatType):""},str2value:function(a){var b=this.parseDate(a,this.parsedFormat);return b?this.fromUTC(b):null},value2submit:function(a){return this.value2str(a)},value2input:function(a){a&&this.$input.data("datetimepicker").setDate(a)},input2value:function(){var a=this.$input.data("datetimepicker");return a.date?a.getDate():null},activate:function(){},clear:function(){this.$input.data("datetimepicker").date=null,this.$input.find(".active").removeClass("active"),this.options.showbuttons||this.$input.closest("form").submit()},autosubmit:function(){this.$input.on("mouseup",".minute",function(){var b=a(this).closest("form");setTimeout(function(){b.submit()},200)})},toUTC:function(a){return a?new Date(a.valueOf()-6e4*a.getTimezoneOffset()):a},fromUTC:function(a){return a?new Date(a.valueOf()+6e4*a.getTimezoneOffset()):a},parseDate:function(a,b){var c,d=null;return a&&(d=this.dpg.parseDate(a,b,this.options.datetimepicker.language,this.options.formatType),"string"==typeof a&&(c=this.dpg.formatDate(d,b,this.options.datetimepicker.language,this.options.formatType),a!==c&&(d=null))),d}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<div class="editable-date well"></div>',inputclass:null,format:"yyyy-mm-dd hh:ii",formatType:"standard",viewformat:null,datetimepicker:{todayHighlight:!1,autoclose:!1},clear:"&times; clear"}),a.fn.editabletypes.datetime=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("datetimefield",a,b.defaults),this.initPicker(a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.datetime),a.extend(b.prototype,{render:function(){this.$input=this.$tpl.find("input"),this.setClass(),this.setAttr("placeholder"),this.$tpl.datetimepicker(this.options.datetimepicker),this.$input.off("focus keydown"),this.$input.keyup(a.proxy(function(){this.$tpl.removeData("date"),this.$tpl.datetimepicker("update")},this))},value2input:function(a){this.$input.val(this.value2html(a)),this.$tpl.datetimepicker("update")},input2value:function(){return this.html2value(this.$input.val())},activate:function(){a.fn.editabletypes.text.prototype.activate.call(this)},autosubmit:function(){}}),b.defaults=a.extend({},a.fn.editabletypes.datetime.defaults,{tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',inputclass:"input-medium",datetimepicker:{todayHighlight:!1,autoclose:!0}}),a.fn.editabletypes.datetimefield=b}(window.jQuery);;
/**
 * Featherlight - ultra slim jQuery lightbox
 * Version 1.7.13 - http://noelboss.github.io/featherlight/
 *
 * Copyright 2018, Noël Raoul Bossart (http://www.noelboss.com)
 * MIT Licensed.
**/
!function(a){"use strict";function b(a,c){if(!(this instanceof b)){var d=new b(a,c);return d.open(),d}this.id=b.id++,this.setup(a,c),this.chainCallbacks(b._callbackChain)}function c(a,b){var c={};for(var d in a)d in b&&(c[d]=a[d],delete a[d]);return c}function d(a,b){var c={},d=new RegExp("^"+b+"([A-Z])(.*)");for(var e in a){var f=e.match(d);if(f){var g=(f[1]+f[2].replace(/([A-Z])/g,"-$1")).toLowerCase();c[g]=a[e]}}return c}if("undefined"==typeof a)return void("console"in window&&window.console.info("Too much lightness, Featherlight needs jQuery."));if(a.fn.jquery.match(/-ajax/))return void("console"in window&&window.console.info("Featherlight needs regular jQuery, not the slim version."));var e=[],f=function(b){return e=a.grep(e,function(a){return a!==b&&a.$instance.closest("body").length>0})},g={allow:1,allowfullscreen:1,frameborder:1,height:1,longdesc:1,marginheight:1,marginwidth:1,mozallowfullscreen:1,name:1,referrerpolicy:1,sandbox:1,scrolling:1,src:1,srcdoc:1,style:1,webkitallowfullscreen:1,width:1},h={keyup:"onKeyUp",resize:"onResize"},i=function(c){a.each(b.opened().reverse(),function(){return c.isDefaultPrevented()||!1!==this[h[c.type]](c)?void 0:(c.preventDefault(),c.stopPropagation(),!1)})},j=function(c){if(c!==b._globalHandlerInstalled){b._globalHandlerInstalled=c;var d=a.map(h,function(a,c){return c+"."+b.prototype.namespace}).join(" ");a(window)[c?"on":"off"](d,i)}};b.prototype={constructor:b,namespace:"featherlight",targetAttr:"data-featherlight",variant:null,resetCss:!1,background:null,openTrigger:"click",closeTrigger:"click",filter:null,root:"body",openSpeed:250,closeSpeed:250,closeOnClick:"background",closeOnEsc:!0,closeIcon:"&#10005;",loading:"",persist:!1,otherClose:null,beforeOpen:a.noop,beforeContent:a.noop,beforeClose:a.noop,afterOpen:a.noop,afterContent:a.noop,afterClose:a.noop,onKeyUp:a.noop,onResize:a.noop,type:null,contentFilters:["jquery","image","html","ajax","iframe","text"],setup:function(b,c){"object"!=typeof b||b instanceof a!=!1||c||(c=b,b=void 0);var d=a.extend(this,c,{target:b}),e=d.resetCss?d.namespace+"-reset":d.namespace,f=a(d.background||['<div class="'+e+"-loading "+e+'">','<div class="'+e+'-content">','<button class="'+e+"-close-icon "+d.namespace+'-close" aria-label="Close">',d.closeIcon,"</button>",'<div class="'+d.namespace+'-inner">'+d.loading+"</div>","</div>","</div>"].join("")),g="."+d.namespace+"-close"+(d.otherClose?","+d.otherClose:"");return d.$instance=f.clone().addClass(d.variant),d.$instance.on(d.closeTrigger+"."+d.namespace,function(b){if(!b.isDefaultPrevented()){var c=a(b.target);("background"===d.closeOnClick&&c.is("."+d.namespace)||"anywhere"===d.closeOnClick||c.closest(g).length)&&(d.close(b),b.preventDefault())}}),this},getContent:function(){if(this.persist!==!1&&this.$content)return this.$content;var b=this,c=this.constructor.contentFilters,d=function(a){return b.$currentTarget&&b.$currentTarget.attr(a)},e=d(b.targetAttr),f=b.target||e||"",g=c[b.type];if(!g&&f in c&&(g=c[f],f=b.target&&e),f=f||d("href")||"",!g)for(var h in c)b[h]&&(g=c[h],f=b[h]);if(!g){var i=f;if(f=null,a.each(b.contentFilters,function(){return g=c[this],g.test&&(f=g.test(i)),!f&&g.regex&&i.match&&i.match(g.regex)&&(f=i),!f}),!f)return"console"in window&&window.console.error("Featherlight: no content filter found "+(i?' for "'+i+'"':" (no target specified)")),!1}return g.process.call(b,f)},setContent:function(b){return this.$instance.removeClass(this.namespace+"-loading"),this.$instance.toggleClass(this.namespace+"-iframe",b.is("iframe")),this.$instance.find("."+this.namespace+"-inner").not(b).slice(1).remove().end().replaceWith(a.contains(this.$instance[0],b[0])?"":b),this.$content=b.addClass(this.namespace+"-inner"),this},open:function(b){var c=this;if(c.$instance.hide().appendTo(c.root),!(b&&b.isDefaultPrevented()||c.beforeOpen(b)===!1)){b&&b.preventDefault();var d=c.getContent();if(d)return e.push(c),j(!0),c.$instance.fadeIn(c.openSpeed),c.beforeContent(b),a.when(d).always(function(a){c.setContent(a),c.afterContent(b)}).then(c.$instance.promise()).done(function(){c.afterOpen(b)})}return c.$instance.detach(),a.Deferred().reject().promise()},close:function(b){var c=this,d=a.Deferred();return c.beforeClose(b)===!1?d.reject():(0===f(c).length&&j(!1),c.$instance.fadeOut(c.closeSpeed,function(){c.$instance.detach(),c.afterClose(b),d.resolve()})),d.promise()},resize:function(a,b){if(a&&b){this.$content.css("width","").css("height","");var c=Math.max(a/(this.$content.parent().width()-1),b/(this.$content.parent().height()-1));c>1&&(c=b/Math.floor(b/c),this.$content.css("width",""+a/c+"px").css("height",""+b/c+"px"))}},chainCallbacks:function(b){for(var c in b)this[c]=a.proxy(b[c],this,a.proxy(this[c],this))}},a.extend(b,{id:0,autoBind:"[data-featherlight]",defaults:b.prototype,contentFilters:{jquery:{regex:/^[#.]\w/,test:function(b){return b instanceof a&&b},process:function(b){return this.persist!==!1?a(b):a(b).clone(!0)}},image:{regex:/\.(png|jpg|jpeg|gif|tiff?|bmp|svg)(\?\S*)?$/i,process:function(b){var c=this,d=a.Deferred(),e=new Image,f=a('<img src="'+b+'" alt="" class="'+c.namespace+'-image" />');return e.onload=function(){f.naturalWidth=e.width,f.naturalHeight=e.height,d.resolve(f)},e.onerror=function(){d.reject(f)},e.src=b,d.promise()}},html:{regex:/^\s*<[\w!][^<]*>/,process:function(b){return a(b)}},ajax:{regex:/./,process:function(b){var c=a.Deferred(),d=a("<div></div>").load(b,function(a,b){"error"!==b&&c.resolve(d.contents()),c.fail()});return c.promise()}},iframe:{process:function(b){var e=new a.Deferred,f=a("<iframe/>"),h=d(this,"iframe"),i=c(h,g);return f.hide().attr("src",b).attr(i).css(h).on("load",function(){e.resolve(f.show())}).appendTo(this.$instance.find("."+this.namespace+"-content")),e.promise()}},text:{process:function(b){return a("<div>",{text:b})}}},functionAttributes:["beforeOpen","afterOpen","beforeContent","afterContent","beforeClose","afterClose"],readElementConfig:function(b,c){var d=this,e=new RegExp("^data-"+c+"-(.*)"),f={};return b&&b.attributes&&a.each(b.attributes,function(){var b=this.name.match(e);if(b){var c=this.value,g=a.camelCase(b[1]);if(a.inArray(g,d.functionAttributes)>=0)c=new Function(c);else try{c=JSON.parse(c)}catch(h){}f[g]=c}}),f},extend:function(b,c){var d=function(){this.constructor=b};return d.prototype=this.prototype,b.prototype=new d,b.__super__=this.prototype,a.extend(b,this,c),b.defaults=b.prototype,b},attach:function(b,c,d){var e=this;"object"!=typeof c||c instanceof a!=!1||d||(d=c,c=void 0),d=a.extend({},d);var f,g=d.namespace||e.defaults.namespace,h=a.extend({},e.defaults,e.readElementConfig(b[0],g),d),i=function(g){var i=a(g.currentTarget),j=a.extend({$source:b,$currentTarget:i},e.readElementConfig(b[0],h.namespace),e.readElementConfig(g.currentTarget,h.namespace),d),k=f||i.data("featherlight-persisted")||new e(c,j);"shared"===k.persist?f=k:k.persist!==!1&&i.data("featherlight-persisted",k),j.$currentTarget.blur&&j.$currentTarget.blur(),k.open(g)};return b.on(h.openTrigger+"."+h.namespace,h.filter,i),{filter:h.filter,handler:i}},current:function(){var a=this.opened();return a[a.length-1]||null},opened:function(){var b=this;return f(),a.grep(e,function(a){return a instanceof b})},close:function(a){var b=this.current();return b?b.close(a):void 0},_onReady:function(){var b=this;if(b.autoBind){var c=a(b.autoBind);c.each(function(){b.attach(a(this))}),a(document).on("click",b.autoBind,function(d){if(!d.isDefaultPrevented()){var e=a(d.currentTarget),f=c.length;if(c=c.add(e),f!==c.length){var g=b.attach(e);(!g.filter||a(d.target).parentsUntil(e,g.filter).length>0)&&g.handler(d)}}})}},_callbackChain:{onKeyUp:function(b,c){return 27===c.keyCode?(this.closeOnEsc&&a.featherlight.close(c),!1):b(c)},beforeOpen:function(b,c){return a(document.documentElement).addClass("with-featherlight"),this._previouslyActive=document.activeElement,this._$previouslyTabbable=a("a, input, select, textarea, iframe, button, iframe, [contentEditable=true]").not("[tabindex]").not(this.$instance.find("button")),this._$previouslyWithTabIndex=a("[tabindex]").not('[tabindex="-1"]'),this._previousWithTabIndices=this._$previouslyWithTabIndex.map(function(b,c){return a(c).attr("tabindex")}),this._$previouslyWithTabIndex.add(this._$previouslyTabbable).attr("tabindex",-1),document.activeElement.blur&&document.activeElement.blur(),b(c)},afterClose:function(c,d){var e=c(d),f=this;return this._$previouslyTabbable.removeAttr("tabindex"),this._$previouslyWithTabIndex.each(function(b,c){a(c).attr("tabindex",f._previousWithTabIndices[b])}),this._previouslyActive.focus(),0===b.opened().length&&a(document.documentElement).removeClass("with-featherlight"),e},onResize:function(a,b){return this.resize(this.$content.naturalWidth,this.$content.naturalHeight),a(b)},afterContent:function(a,b){var c=a(b);return this.$instance.find("[autofocus]:not([disabled])").focus(),this.onResize(b),c}}}),a.featherlight=b,a.fn.featherlight=function(a,c){return b.attach(this,a,c),this},a(document).ready(function(){b._onReady()})}(jQuery);;
/**
	Kailash Nadh,	http://kailashnadh.name
	February 2011
	Smooth popup dialog for jQuery

	License:	GNU Public License: http://www.fsf.org/copyleft/gpl.html
	
	v1.3	February 6 2011	-	Rewrote the whole plugin to comply with jQuery's plugin standards
	v1.2	September 2 2009
**/

(function ($) { $.jqDialog = { labels: { ok: 'Ok', yes: 'Yes', no: 'No', cancel: 'Cancel', x: 'X' }, ids: { div_box: 'jqDialog_box', div_content: 'jqDialog_content', div_options: 'jqDialog_options', bt_close: 'jqDialog_close', bt_yes: 'jqDialog_yes', bt_no: 'jqDialog_no', bt_ok: 'jqDialog_ok', bt_ancel: 'jqDialog_ok', input: 'jqDialog_input' }, confirm: function (message, callback_yes, callback_no) { var t = this; t.create(message); t.parts.bt_ok.hide(); t.parts.bt_cancel.hide(); t.parts.bt_yes.show(); t.parts.bt_no.show(); t.parts.bt_yes.focus(); t.parts.bt_yes.unbind().click(function () { t.close(); if (callback_yes) callback_yes(); }); t.parts.bt_no.unbind().click(function () { t.close(); if (callback_no) callback_no(); }); }, prompt: function (message, content, callback_ok, callback_cancel) { var t = this; t.create($("<div>").append(message).append($("<div>").append(t.parts.input.val(content)))); t.parts.bt_yes.hide(); t.parts.bt_no.hide(); t.parts.bt_ok.show(); t.parts.bt_cancel.show(); t.parts.input.focus(); t.parts.bt_ok.unbind().click(function () { t.close(); if (callback_ok) callback_ok(t.parts.input.val()); }); t.parts.bt_cancel.unbind().click(function () { t.close(); if (callback_cancel) callback_cancel(); }); }, alert: function (content, callback_ok) { var t = this; t.create(content); t.parts.bt_cancel.hide(); t.parts.bt_yes.hide(); t.parts.bt_no.hide(); t.parts.bt_ok.show(); t.parts.bt_ok.focus(); t.parts.bt_ok.unbind().click(function () { t.close(); if (callback_ok) callback_ok(); }); }, content: function (content, close_seconds) { var t = this; t.create(content); t.parts.div_options.hide(); }, notify: function (content, close_seconds) { var t = this; t.content(content); t.parts.bt_close.focus(); if (close_seconds) t.close_timer = setTimeout(function () { t.close(); }, close_seconds * 1000); }, create: function (content) { var t = this; t.check(); t.maintainPosition(t.parts.div_box); clearTimeout(t.close_timer); t.parts.div_content.html(content); t.parts.div_options.show(); t.parts.div_box.fadeIn('fast'); }, close: function () { var t = this; t.parts.div_box.fadeOut('fast'); t.clearPosition(); }, clearPosition: function () { $(window).unbind('scroll.jqDialog'); }, makeCenter: function (object) { object.css({ top: ((($(window).height() / 2) - (object.height() / 2))) + ($(document).scrollTop()) + 'px', left: ((($(window).width() / 2) - (object.width() / 2))) + ($(document).scrollLeft()) + 'px' }); }, maintainPosition: function (object) { var t = this; t.makeCenter(object); $(window).bind('scroll.jqDialog', function () { t.makeCenter(object); }); }, init_done: false, check: function () { var t = this; if (t.init_done) return; else { t.init_done = true; } $('body').append(t.parts.div_box); }, init: function () { var t = this; t.parts = {}; t.parts.div_box = $("<div>").attr({ id: t.ids.div_box }); t.parts.div_content = $("<div>").attr({ id: t.ids.div_content }); t.parts.div_options = $("<div>").attr({ id: t.ids.div_options }); t.parts.bt_yes = $("<button>").attr({ id: t.ids.bt_yes }).append(t.labels.yes); t.parts.bt_no = $("<button>").attr({ id: t.ids.bt_no }).append(t.labels.no); t.parts.bt_ok = $("<button>").attr({ id: t.ids.bt_ok }).append(t.labels.ok); t.parts.bt_cancel = $("<button>").attr({ id: t.ids.bt_cancel }).append(t.labels.cancel); t.parts.input = $("<input>").attr({ id: t.ids.input }); t.parts.bt_close = $("<button>").attr({ id: t.ids.bt_close }).append(t.labels.x).click(function () { t.close(); }); t.parts.div_box.append(t.parts.bt_close).append(t.parts.div_content).append(t.parts.div_options.append(t.parts.bt_yes).append(t.parts.bt_no).append(t.parts.bt_ok).append(t.parts.bt_cancel)); t.parts.div_box.hide(); } }; $.jqDialog.init(); })(jQuery);;
/**
* Author: Fastforza 
* Date: 9-12-2009
* Version: 1
* New editor, cross browser compatible. Includes BB insert, smiley insert; use insert_text
*/

//var TextBoxName = 'ctl3_GetMessage';

// Startup variables
var imageTag = false;
var theSelection = false;

// Store the following form variables inside the web control
//var form_name = '';
//var text_name = '';

var bbcodeEnabled = true;
// Check for Browser & Platform for PC & IE specific bits
// More details from: http://www.mozilla.org/docs/web-developer/sniffer/browser_type.html
var clientPC = navigator.userAgent.toLowerCase(); // Get client info
var clientVer = parseInt(navigator.appVersion); // Get browser version

var is_ie = ((clientPC.indexOf('msie') != -1) && (clientPC.indexOf('opera') == -1));
var is_win = ((clientPC.indexOf('win') != -1) || (clientPC.indexOf('16bit') != -1));
var baseHeight;

// Define the bbCode tags
var bbcode = new Array();
var bbtags = new Array('[b]', '[/b]', '[i]', '[/i]', '[u]', '[/u]', '[quote]', '[/quote]', '[code]', '[/code]', '[list]', '[/list]', '[list=]', '[/list]', '[img]', '[/img]', '[url]', '[/url]', '[flash=]', '[/flash]', '[size=]', '[/size]', '[track]', '[/track]', '[track="short"]', '[/track]', '[track="full"]', '[/track]', '[left]', '[/left]', '[center]', '[/center]', '[right]', '[/right]', '[s]', '[/s]', '[list][item]', '[/item][/list]', '[user]', '[/user]');

/**
* Bbstyle Guide to Buttons
* =============================================
* Bold: bbstyle(0) *
* Italic: bbstyle(2) *
* Underline: bbstyle(4) *
* Quote: bbstyle(6)
* Code: bbstyle(8)
* List: bbstyle(10)
* List w/Ele: bbstyle(12)
* List w/Ite: bbstyle(-1)
* Image: bbstyle(14) *
* Url: bbstyle(16) *
* Flash: bbstyle(18)
* Size: bbfontstyle('[size=' + this.form.addbbcode20.options[this.form.addbbcode20.selectedIndex].value + ']', '[/size]');this.form.addbbcode20.selectedIndex = 2;
* Size: Requires a list of increasing values to 100.
*
* Begin TMX Codes & TMX Formatting Codes
* =============================================
* Track: bbstyle(22) *
* Track Short: bbstyle(24) *
* Track Full: bbstyle(26) 
* Left Textl: bbstyle(28) *
* Center Textl: bbstyle(30) *
* Right Text: bbstyle(32) *
* Strike: bbstyle(34) *
* List: bbstyle(36) *
* User: bbstyle(38)
*/

/**
* Shows the help messages in the helpline window
*/
function helpline(help) {
	document.forms[form_name].helpbox.value = help_line[help];
}

/**
* Fix a bug involving the TextRange object. From
* http://www.frostjedi.com/terra/scripts/demo/caretBug.html
*/
function initInsertions() {
	var doc;

	if (document.forms[form_name]) {
		doc = document;
	}
	else {
		doc = opener.document;
	}

	var textarea = doc.forms[form_name].elements[text_name];

	if (is_ie && typeof (baseHeight) != 'number') {
		textarea.focus();
		baseHeight = doc.selection.createRange().duplicate().boundingHeight;

		if (!document.forms[form_name]) {
			document.body.focus();
		}
	}
}

/**
* bbstyle
*/
function bbstyle(bbnumber) {
	if (bbnumber != -1) {
		bbfontstyle(bbtags[bbnumber], bbtags[bbnumber + 1]);
	}
	else {
		insert_text('[*]');
		document.forms[form_name].elements[text_name].focus();
	}
}

/**
* Apply bbcodes
*/
function bbfontstyle(bbopen, bbclose) {
	theSelection = false;

	var textarea = document.forms[form_name].elements[text_name];

	textarea.focus();

	if ((clientVer >= 4) && is_ie && is_win) {
		// Get text selection
		theSelection = document.selection.createRange().text;

		if (theSelection) {
			// Add tags around selection
			document.selection.createRange().text = bbopen + theSelection + bbclose;
			document.forms[form_name].elements[text_name].focus();
			theSelection = '';
			return;
		}
	}
	else if (document.forms[form_name].elements[text_name].selectionEnd && (document.forms[form_name].elements[text_name].selectionEnd - document.forms[form_name].elements[text_name].selectionStart > 0)) {
		mozWrap(document.forms[form_name].elements[text_name], bbopen, bbclose);
		document.forms[form_name].elements[text_name].focus();
		theSelection = '';
		return;
	}

	//The new position for the cursor after adding the bbcode
	var caret_pos = getCaretPosition(textarea).start;
	var new_pos = caret_pos + bbopen.length;

	// Open tag
	insert_text(bbopen + bbclose);

	// Center the cursor when we don't have a selection
	// Gecko and proper browsers
	if (!isNaN(textarea.selectionStart)) {
		textarea.selectionStart = new_pos;
		textarea.selectionEnd = new_pos;
	}
	// IE
	else if (document.selection) {
		var range = textarea.createTextRange();
		range.move("character", new_pos);
		range.select();
		storeCaret(textarea);
	}

	textarea.focus();
	return;
}

/**
* Insert text at position
*/
function insert_text(text, spaces, popup) {
	var textarea;

	if (!popup) {
		textarea = document.forms[form_name].elements[text_name];
	}
	else {
		textarea = opener.document.forms[form_name].elements[text_name];
	}
	if (spaces) {
		text = ' ' + text + ' ';
	}

	if (!isNaN(textarea.selectionStart)) {
		var sel_start = textarea.selectionStart;
		var sel_end = textarea.selectionEnd;

		mozWrap(textarea, text, '')
		textarea.selectionStart = sel_start + text.length;
		textarea.selectionEnd = sel_end + text.length;
	}
	else if (textarea.createTextRange && textarea.caretPos) {
		if (baseHeight != textarea.caretPos.boundingHeight) {
			textarea.focus();
			storeCaret(textarea);
		}

		var caret_pos = textarea.caretPos;
		caret_pos.text = caret_pos.text.charAt(caret_pos.text.length - 1) == ' ' ? caret_pos.text + text + ' ' : caret_pos.text + text;
	}
	else {
		textarea.value = textarea.value + text;
	}
	if (!popup) {
		textarea.focus();
	}
}

/**
* Add inline attachment at position
*/
function attach_inline(index, filename) {
	insert_text('[attachment=' + index + ']' + filename + '[/attachment]');
	document.forms[form_name].elements[text_name].focus();
}

/**
* Add quote text to message
*/
function addquote(post_id, username) {
	var message_name = 'message_' + post_id;
	var theSelection = '';
	var divarea = false;

	if (document.all) {
		divarea = document.all[message_name];
	}
	else {
		divarea = document.getElementById(message_name);
	}

	// Get text selection - not only the post content :(
	if (window.getSelection) {
		theSelection = window.getSelection().toString();
	}
	else if (document.getSelection) {
		theSelection = document.getSelection();
	}
	else if (document.selection) {
		theSelection = document.selection.createRange().text;
	}

	if (theSelection == '' || typeof theSelection == 'undefined' || theSelection == null) {
		if (divarea.innerHTML) {
			theSelection = divarea.innerHTML.replace(/<br>/ig, '\n');
			theSelection = theSelection.replace(/<br\/>/ig, '\n');
			theSelection = theSelection.replace(/&lt\;/ig, '<');
			theSelection = theSelection.replace(/&gt\;/ig, '>');
			theSelection = theSelection.replace(/&amp\;/ig, '&');
			theSelection = theSelection.replace(/&nbsp\;/ig, ' ');
		}
		else if (document.all) {
			theSelection = divarea.innerText;
		}
		else if (divarea.textContent) {
			theSelection = divarea.textContent;
		}
		else if (divarea.firstChild.nodeValue) {
			theSelection = divarea.firstChild.nodeValue;
		}
	}

	if (theSelection) {
		if (bbcodeEnabled) {
			insert_text('[quote="' + username + '"]' + theSelection + '[/quote]');
		}
		else {
			var lines = split_lines(theSelection);
			for (i = 0; i < lines.length; i++) {
				insert_text('> ' + lines[i] + '\n')
			}
		}
	}

	return;
}

function split_lines(text) {
	var lines = text.split('\n');
	var splitLines = new Array();
	var j = 0;
	for (i = 0; i < lines.length; i++) {
		if (lines[i].length <= 80) {
			splitLines[j] = lines[i];
			j++;
		}
		else {
			var line = lines[i];
			do {
				var splitAt = line.indexOf(' ', 80);

				if (splitAt == -1) {
					splitLines[j] = line;
					j++
				}
				else {
					splitLines[j] = line.substring(0, splitAt);
					line = line.substring(splitAt);
					j++;
				}
			}
			while (splitAt != -1)
		}
	}
	return splitLines;
}
/**
* From http://www.massless.org/mozedit/
*/
function mozWrap(txtarea, open, close) {
	var selLength = (typeof (txtarea.textLength) == 'undefined') ? txtarea.value.length : txtarea.textLength;
	var selStart = txtarea.selectionStart;
	var selEnd = txtarea.selectionEnd;
	var scrollTop = txtarea.scrollTop;

	if (selEnd == 1 || selEnd == 2) {
		selEnd = selLength;
	}

	var s1 = (txtarea.value).substring(0, selStart);
	var s2 = (txtarea.value).substring(selStart, selEnd)
	var s3 = (txtarea.value).substring(selEnd, selLength);

	txtarea.value = s1 + open + s2 + close + s3;
	txtarea.selectionStart = selEnd + open.length + close.length;
	txtarea.selectionEnd = txtarea.selectionStart;
	txtarea.focus();
	txtarea.scrollTop = scrollTop;

	return;
}

/**
* Insert at Caret position. Code from
* http://www.faqts.com/knowledge_base/view.phtml/aid/1052/fid/130
*/
function storeCaret(textEl) {
	if (textEl.createTextRange) {
		textEl.caretPos = document.selection.createRange().duplicate();
	}
}

/**
* Color pallette
*/
function colorPalette(dir, width, height) {
	var r = 0, g = 0, b = 0;
	var numberList = new Array(6);
	var color = '';

	numberList[0] = '00';
	numberList[1] = '40';
	numberList[2] = '80';
	numberList[3] = 'BF';
	numberList[4] = 'FF';

	document.writeln('<table cellspacing="1" cellpadding="0" border="0">');

	for (r = 0; r < 5; r++) {
		if (dir == 'h') {
			document.writeln('<tr>');
		}

		for (g = 0; g < 5; g++) {
			if (dir == 'v') {
				document.writeln('<tr>');
			}

			for (b = 0; b < 5; b++) {
				color = String(numberList[r]) + String(numberList[g]) + String(numberList[b]);
				document.write('<td bgcolor="#' + color + '" style="width: ' + width + 'px; height: ' + height + 'px;">');
				document.write('<a href="#" onclick="bbfontstyle(\'[color=#' + color + ']\', \'[/color]\'); return false;"><img src="images/spacer.gif" width="' + width + '" height="' + height + '" alt="#' + color + '" title="#' + color + '" /></a>');
				document.writeln('</td>');
			}

			if (dir == 'v') {
				document.writeln('</tr>');
			}
		}

		if (dir == 'h') {
			document.writeln('</tr>');
		}
	}
	document.writeln('</table>');
}


/**
* Caret Position object
*/
function caretPosition() {
	var start = null;
	var end = null;
}


/**
* Get the caret position in an textarea
*/
function getCaretPosition(txtarea) {
	var caretPos = new caretPosition();

	// simple Gecko/Opera way
	if (txtarea.selectionStart || txtarea.selectionStart == 0) {
		caretPos.start = txtarea.selectionStart;
		caretPos.end = txtarea.selectionEnd;
	}
	// dirty and slow IE way
	else if (document.selection) {

		// get current selection
		var range = document.selection.createRange();

		// a new selection of the whole textarea
		var range_all = document.body.createTextRange();
		range_all.moveToElementText(txtarea);

		// calculate selection start point by moving beginning of range_all to beginning of range
		var sel_start;
		for (sel_start = 0; range_all.compareEndPoints('StartToStart', range) < 0; sel_start++) {
			range_all.moveStart('character', 1);
		}

		txtarea.sel_start = sel_start;

		// we ignore the end value for IE, this is already dirty enough and we don't need it
		caretPos.start = txtarea.sel_start;
		caretPos.end = txtarea.sel_start;
	}

	return caretPos;
};
// â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â” \\
// â”‚ RaphaÃ«l 2.1.0 - JavaScript Vector Library                          â”‚ \\
// â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤ \\
// â”‚ Copyright Â© 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com)    â”‚ \\
// â”‚ Copyright Â© 2008-2012 Sencha Labs (http://sencha.com)              â”‚ \\
// â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤ \\
// â”‚ Licensed under the MIT (http://raphaeljs.com/license.html) license.â”‚ \\
// â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜ \\

(function (a) { var b = "0.3.4", c = "hasOwnProperty", d = /[\.\/]/, e = "*", f = function () { }, g = function (a, b) { return a - b }, h, i, j = { n: {} }, k = function (a, b) { var c = j, d = i, e = Array.prototype.slice.call(arguments, 2), f = k.listeners(a), l = 0, m = !1, n, o = [], p = {}, q = [], r = h, s = []; h = a, i = 0; for (var t = 0, u = f.length; t < u; t++)"zIndex" in f[t] && (o.push(f[t].zIndex), f[t].zIndex < 0 && (p[f[t].zIndex] = f[t])); o.sort(g); while (o[l] < 0) { n = p[o[l++]], q.push(n.apply(b, e)); if (i) { i = d; return q } } for (t = 0; t < u; t++) { n = f[t]; if ("zIndex" in n) if (n.zIndex == o[l]) { q.push(n.apply(b, e)); if (i) break; do { l++, n = p[o[l]], n && q.push(n.apply(b, e)); if (i) break } while (n) } else p[n.zIndex] = n; else { q.push(n.apply(b, e)); if (i) break } } i = d, h = r; return q.length ? q : null }; k.listeners = function (a) { var b = a.split(d), c = j, f, g, h, i, k, l, m, n, o = [c], p = []; for (i = 0, k = b.length; i < k; i++) { n = []; for (l = 0, m = o.length; l < m; l++) { c = o[l].n, g = [c[b[i]], c[e]], h = 2; while (h--) f = g[h], f && (n.push(f), p = p.concat(f.f || [])) } o = n } return p }, k.on = function (a, b) { var c = a.split(d), e = j; for (var g = 0, h = c.length; g < h; g++)e = e.n, !e[c[g]] && (e[c[g]] = { n: {} }), e = e[c[g]]; e.f = e.f || []; for (g = 0, h = e.f.length; g < h; g++)if (e.f[g] == b) return f; e.f.push(b); return function (a) { +a == +a && (b.zIndex = +a) } }, k.stop = function () { i = 1 }, k.nt = function (a) { if (a) return (new RegExp("(?:\\.|\\/|^)" + a + "(?:\\.|\\/|$)")).test(h); return h }, k.off = k.unbind = function (a, b) { var f = a.split(d), g, h, i, k, l, m, n, o = [j]; for (k = 0, l = f.length; k < l; k++)for (m = 0; m < o.length; m += i.length - 2) { i = [m, 1], g = o[m].n; if (f[k] != e) g[f[k]] && i.push(g[f[k]]); else for (h in g) g[c](h) && i.push(g[h]); o.splice.apply(o, i) } for (k = 0, l = o.length; k < l; k++) { g = o[k]; while (g.n) { if (b) { if (g.f) { for (m = 0, n = g.f.length; m < n; m++)if (g.f[m] == b) { g.f.splice(m, 1); break } !g.f.length && delete g.f } for (h in g.n) if (g.n[c](h) && g.n[h].f) { var p = g.n[h].f; for (m = 0, n = p.length; m < n; m++)if (p[m] == b) { p.splice(m, 1); break } !p.length && delete g.n[h].f } } else { delete g.f; for (h in g.n) g.n[c](h) && g.n[h].f && delete g.n[h].f } g = g.n } } }, k.once = function (a, b) { var c = function () { var d = b.apply(this, arguments); k.unbind(a, c); return d }; return k.on(a, c) }, k.version = b, k.toString = function () { return "You are running Eve " + b }, typeof module != "undefined" && module.exports ? module.exports = k : typeof define != "undefined" ? define("eve", [], function () { return k }) : a.eve = k })(this), function () { function cF(a) { for (var b = 0; b < cy.length; b++)cy[b].el.paper == a && cy.splice(b--, 1) } function cE(b, d, e, f, h, i) { e = Q(e); var j, k, l, m = [], o, p, q, t = b.ms, u = {}, v = {}, w = {}; if (f) for (y = 0, z = cy.length; y < z; y++) { var x = cy[y]; if (x.el.id == d.id && x.anim == b) { x.percent != e ? (cy.splice(y, 1), l = 1) : k = x, d.attr(x.totalOrigin); break } } else f = +v; for (var y = 0, z = b.percents.length; y < z; y++) { if (b.percents[y] == e || b.percents[y] > f * b.top) { e = b.percents[y], p = b.percents[y - 1] || 0, t = t / b.top * (e - p), o = b.percents[y + 1], j = b.anim[e]; break } f && d.attr(b.anim[b.percents[y]]) } if (!!j) { if (!k) { for (var A in j) if (j[g](A)) if (U[g](A) || d.paper.customAttributes[g](A)) { u[A] = d.attr(A), u[A] == null && (u[A] = T[A]), v[A] = j[A]; switch (U[A]) { case C: w[A] = (v[A] - u[A]) / t; break; case "colour": u[A] = a.getRGB(u[A]); var B = a.getRGB(v[A]); w[A] = { r: (B.r - u[A].r) / t, g: (B.g - u[A].g) / t, b: (B.b - u[A].b) / t }; break; case "path": var D = bR(u[A], v[A]), E = D[1]; u[A] = D[0], w[A] = []; for (y = 0, z = u[A].length; y < z; y++) { w[A][y] = [0]; for (var F = 1, G = u[A][y].length; F < G; F++)w[A][y][F] = (E[y][F] - u[A][y][F]) / t } break; case "transform": var H = d._, I = ca(H[A], v[A]); if (I) { u[A] = I.from, v[A] = I.to, w[A] = [], w[A].real = !0; for (y = 0, z = u[A].length; y < z; y++) { w[A][y] = [u[A][y][0]]; for (F = 1, G = u[A][y].length; F < G; F++)w[A][y][F] = (v[A][y][F] - u[A][y][F]) / t } } else { var J = d.matrix || new cb, K = { _: { transform: H.transform }, getBBox: function () { return d.getBBox(1) } }; u[A] = [J.a, J.b, J.c, J.d, J.e, J.f], b$(K, v[A]), v[A] = K._.transform, w[A] = [(K.matrix.a - J.a) / t, (K.matrix.b - J.b) / t, (K.matrix.c - J.c) / t, (K.matrix.d - J.d) / t, (K.matrix.e - J.e) / t, (K.matrix.f - J.f) / t] } break; case "csv": var L = r(j[A])[s](c), M = r(u[A])[s](c); if (A == "clip-rect") { u[A] = M, w[A] = [], y = M.length; while (y--) w[A][y] = (L[y] - u[A][y]) / t } v[A] = L; break; default: L = [][n](j[A]), M = [][n](u[A]), w[A] = [], y = d.paper.customAttributes[A].length; while (y--) w[A][y] = ((L[y] || 0) - (M[y] || 0)) / t } } var O = j.easing, P = a.easing_formulas[O]; if (!P) { P = r(O).match(N); if (P && P.length == 5) { var R = P; P = function (a) { return cC(a, +R[1], +R[2], +R[3], +R[4], t) } } else P = bf } q = j.start || b.start || +(new Date), x = { anim: b, percent: e, timestamp: q, start: q + (b.del || 0), status: 0, initstatus: f || 0, stop: !1, ms: t, easing: P, from: u, diff: w, to: v, el: d, callback: j.callback, prev: p, next: o, repeat: i || b.times, origin: d.attr(), totalOrigin: h }, cy.push(x); if (f && !k && !l) { x.stop = !0, x.start = new Date - t * f; if (cy.length == 1) return cA() } l && (x.start = new Date - x.ms * f), cy.length == 1 && cz(cA) } else k.initstatus = f, k.start = new Date - k.ms * f; eve("raphael.anim.start." + d.id, d, b) } } function cD(a, b) { var c = [], d = {}; this.ms = b, this.times = 1; if (a) { for (var e in a) a[g](e) && (d[Q(e)] = a[e], c.push(Q(e))); c.sort(bd) } this.anim = d, this.top = c[c.length - 1], this.percents = c } function cC(a, b, c, d, e, f) { function o(a, b) { var c, d, e, f, j, k; for (e = a, k = 0; k < 8; k++) { f = m(e) - a; if (z(f) < b) return e; j = (3 * i * e + 2 * h) * e + g; if (z(j) < 1e-6) break; e = e - f / j } c = 0, d = 1, e = a; if (e < c) return c; if (e > d) return d; while (c < d) { f = m(e); if (z(f - a) < b) return e; a > f ? c = e : d = e, e = (d - c) / 2 + c } return e } function n(a, b) { var c = o(a, b); return ((l * c + k) * c + j) * c } function m(a) { return ((i * a + h) * a + g) * a } var g = 3 * b, h = 3 * (d - b) - g, i = 1 - g - h, j = 3 * c, k = 3 * (e - c) - j, l = 1 - j - k; return n(a, 1 / (200 * f)) } function cq() { return this.x + q + this.y + q + this.width + " Ã— " + this.height } function cp() { return this.x + q + this.y } function cb(a, b, c, d, e, f) { a != null ? (this.a = +a, this.b = +b, this.c = +c, this.d = +d, this.e = +e, this.f = +f) : (this.a = 1, this.b = 0, this.c = 0, this.d = 1, this.e = 0, this.f = 0) } function bH(b, c, d) { b = a._path2curve(b), c = a._path2curve(c); var e, f, g, h, i, j, k, l, m, n, o = d ? 0 : []; for (var p = 0, q = b.length; p < q; p++) { var r = b[p]; if (r[0] == "M") e = i = r[1], f = j = r[2]; else { r[0] == "C" ? (m = [e, f].concat(r.slice(1)), e = m[6], f = m[7]) : (m = [e, f, e, f, i, j, i, j], e = i, f = j); for (var s = 0, t = c.length; s < t; s++) { var u = c[s]; if (u[0] == "M") g = k = u[1], h = l = u[2]; else { u[0] == "C" ? (n = [g, h].concat(u.slice(1)), g = n[6], h = n[7]) : (n = [g, h, g, h, k, l, k, l], g = k, h = l); var v = bG(m, n, d); if (d) o += v; else { for (var w = 0, x = v.length; w < x; w++)v[w].segment1 = p, v[w].segment2 = s, v[w].bez1 = m, v[w].bez2 = n; o = o.concat(v) } } } } } return o } function bG(b, c, d) { var e = a.bezierBBox(b), f = a.bezierBBox(c); if (!a.isBBoxIntersect(e, f)) return d ? 0 : []; var g = bB.apply(0, b), h = bB.apply(0, c), i = ~~(g / 5), j = ~~(h / 5), k = [], l = [], m = {}, n = d ? 0 : []; for (var o = 0; o < i + 1; o++) { var p = a.findDotsAtSegment.apply(a, b.concat(o / i)); k.push({ x: p.x, y: p.y, t: o / i }) } for (o = 0; o < j + 1; o++)p = a.findDotsAtSegment.apply(a, c.concat(o / j)), l.push({ x: p.x, y: p.y, t: o / j }); for (o = 0; o < i; o++)for (var q = 0; q < j; q++) { var r = k[o], s = k[o + 1], t = l[q], u = l[q + 1], v = z(s.x - r.x) < .001 ? "y" : "x", w = z(u.x - t.x) < .001 ? "y" : "x", x = bD(r.x, r.y, s.x, s.y, t.x, t.y, u.x, u.y); if (x) { if (m[x.x.toFixed(4)] == x.y.toFixed(4)) continue; m[x.x.toFixed(4)] = x.y.toFixed(4); var y = r.t + z((x[v] - r[v]) / (s[v] - r[v])) * (s.t - r.t), A = t.t + z((x[w] - t[w]) / (u[w] - t[w])) * (u.t - t.t); y >= 0 && y <= 1 && A >= 0 && A <= 1 && (d ? n++ : n.push({ x: x.x, y: x.y, t1: y, t2: A })) } } return n } function bF(a, b) { return bG(a, b, 1) } function bE(a, b) { return bG(a, b) } function bD(a, b, c, d, e, f, g, h) { if (!(x(a, c) < y(e, g) || y(a, c) > x(e, g) || x(b, d) < y(f, h) || y(b, d) > x(f, h))) { var i = (a * d - b * c) * (e - g) - (a - c) * (e * h - f * g), j = (a * d - b * c) * (f - h) - (b - d) * (e * h - f * g), k = (a - c) * (f - h) - (b - d) * (e - g); if (!k) return; var l = i / k, m = j / k, n = +l.toFixed(2), o = +m.toFixed(2); if (n < +y(a, c).toFixed(2) || n > +x(a, c).toFixed(2) || n < +y(e, g).toFixed(2) || n > +x(e, g).toFixed(2) || o < +y(b, d).toFixed(2) || o > +x(b, d).toFixed(2) || o < +y(f, h).toFixed(2) || o > +x(f, h).toFixed(2)) return; return { x: l, y: m } } } function bC(a, b, c, d, e, f, g, h, i) { if (!(i < 0 || bB(a, b, c, d, e, f, g, h) < i)) { var j = 1, k = j / 2, l = j - k, m, n = .01; m = bB(a, b, c, d, e, f, g, h, l); while (z(m - i) > n) k /= 2, l += (m < i ? 1 : -1) * k, m = bB(a, b, c, d, e, f, g, h, l); return l } } function bB(a, b, c, d, e, f, g, h, i) { i == null && (i = 1), i = i > 1 ? 1 : i < 0 ? 0 : i; var j = i / 2, k = 12, l = [-0.1252, .1252, -0.3678, .3678, -0.5873, .5873, -0.7699, .7699, -0.9041, .9041, -0.9816, .9816], m = [.2491, .2491, .2335, .2335, .2032, .2032, .1601, .1601, .1069, .1069, .0472, .0472], n = 0; for (var o = 0; o < k; o++) { var p = j * l[o] + j, q = bA(p, a, c, e, g), r = bA(p, b, d, f, h), s = q * q + r * r; n += m[o] * w.sqrt(s) } return j * n } function bA(a, b, c, d, e) { var f = -3 * b + 9 * c - 9 * d + 3 * e, g = a * f + 6 * b - 12 * c + 6 * d; return a * g - 3 * b + 3 * c } function by(a, b) { var c = []; for (var d = 0, e = a.length; e - 2 * !b > d; d += 2) { var f = [{ x: +a[d - 2], y: +a[d - 1] }, { x: +a[d], y: +a[d + 1] }, { x: +a[d + 2], y: +a[d + 3] }, { x: +a[d + 4], y: +a[d + 5] }]; b ? d ? e - 4 == d ? f[3] = { x: +a[0], y: +a[1] } : e - 2 == d && (f[2] = { x: +a[0], y: +a[1] }, f[3] = { x: +a[2], y: +a[3] }) : f[0] = { x: +a[e - 2], y: +a[e - 1] } : e - 4 == d ? f[3] = f[2] : d || (f[0] = { x: +a[d], y: +a[d + 1] }), c.push(["C", (-f[0].x + 6 * f[1].x + f[2].x) / 6, (-f[0].y + 6 * f[1].y + f[2].y) / 6, (f[1].x + 6 * f[2].x - f[3].x) / 6, (f[1].y + 6 * f[2].y - f[3].y) / 6, f[2].x, f[2].y]) } return c } function bx() { return this.hex } function bv(a, b, c) { function d() { var e = Array.prototype.slice.call(arguments, 0), f = e.join("â€"), h = d.cache = d.cache || {}, i = d.count = d.count || []; if (h[g](f)) { bu(i, f); return c ? c(h[f]) : h[f] } i.length >= 1e3 && delete h[i.shift()], i.push(f), h[f] = a[m](b, e); return c ? c(h[f]) : h[f] } return d } function bu(a, b) { for (var c = 0, d = a.length; c < d; c++)if (a[c] === b) return a.push(a.splice(c, 1)[0]) } function bm(a) { if (Object(a) !== a) return a; var b = new a.constructor; for (var c in a) a[g](c) && (b[c] = bm(a[c])); return b } function a(c) { if (a.is(c, "function")) return b ? c() : eve.on("raphael.DOMload", c); if (a.is(c, E)) return a._engine.create[m](a, c.splice(0, 3 + a.is(c[0], C))).add(c); var d = Array.prototype.slice.call(arguments, 0); if (a.is(d[d.length - 1], "function")) { var e = d.pop(); return b ? e.call(a._engine.create[m](a, d)) : eve.on("raphael.DOMload", function () { e.call(a._engine.create[m](a, d)) }) } return a._engine.create[m](a, arguments) } a.version = "2.1.0", a.eve = eve; var b, c = /[, ]+/, d = { circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1 }, e = /\{(\d+)\}/g, f = "prototype", g = "hasOwnProperty", h = { doc: document, win: window }, i = { was: Object.prototype[g].call(h.win, "Raphael"), is: h.win.Raphael }, j = function () { this.ca = this.customAttributes = {} }, k, l = "appendChild", m = "apply", n = "concat", o = "createTouch" in h.doc, p = "", q = " ", r = String, s = "split", t = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[s](q), u = { mousedown: "touchstart", mousemove: "touchmove", mouseup: "touchend" }, v = r.prototype.toLowerCase, w = Math, x = w.max, y = w.min, z = w.abs, A = w.pow, B = w.PI, C = "number", D = "string", E = "array", F = "toString", G = "fill", H = Object.prototype.toString, I = {}, J = "push", K = a._ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i, L = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i, M = { NaN: 1, Infinity: 1, "-Infinity": 1 }, N = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/, O = w.round, P = "setAttribute", Q = parseFloat, R = parseInt, S = r.prototype.toUpperCase, T = a._availableAttrs = { "arrow-end": "none", "arrow-start": "none", blur: 0, "clip-rect": "0 0 1e9 1e9", cursor: "default", cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '10px "Arial"', "font-family": '"Arial"', "font-size": "10", "font-style": "normal", "font-weight": 400, gradient: 0, height: 0, href: "http://raphaeljs.com/", "letter-spacing": 0, opacity: 1, path: "M0,0", r: 0, rx: 0, ry: 0, src: "", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, target: "_blank", "text-anchor": "middle", title: "Raphael", transform: "", width: 0, x: 0, y: 0 }, U = a._availableAnimAttrs = { blur: C, "clip-rect": "csv", cx: C, cy: C, fill: "colour", "fill-opacity": C, "font-size": C, height: C, opacity: C, path: "path", r: C, rx: C, ry: C, stroke: "colour", "stroke-opacity": C, "stroke-width": C, transform: "transform", width: C, x: C, y: C }, V = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g, W = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/, X = { hs: 1, rg: 1 }, Y = /,?([achlmqrstvxz]),?/gi, Z = /([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig, $ = /([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig, _ = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig, ba = a._radial_gradient = /^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/, bb = {}, bc = function (a, b) { return a.key - b.key }, bd = function (a, b) { return Q(a) - Q(b) }, be = function () { }, bf = function (a) { return a }, bg = a._rectPath = function (a, b, c, d, e) { if (e) return [["M", a + e, b], ["l", c - e * 2, 0], ["a", e, e, 0, 0, 1, e, e], ["l", 0, d - e * 2], ["a", e, e, 0, 0, 1, -e, e], ["l", e * 2 - c, 0], ["a", e, e, 0, 0, 1, -e, -e], ["l", 0, e * 2 - d], ["a", e, e, 0, 0, 1, e, -e], ["z"]]; return [["M", a, b], ["l", c, 0], ["l", 0, d], ["l", -c, 0], ["z"]] }, bh = function (a, b, c, d) { d == null && (d = c); return [["M", a, b], ["m", 0, -d], ["a", c, d, 0, 1, 1, 0, 2 * d], ["a", c, d, 0, 1, 1, 0, -2 * d], ["z"]] }, bi = a._getPath = { path: function (a) { return a.attr("path") }, circle: function (a) { var b = a.attrs; return bh(b.cx, b.cy, b.r) }, ellipse: function (a) { var b = a.attrs; return bh(b.cx, b.cy, b.rx, b.ry) }, rect: function (a) { var b = a.attrs; return bg(b.x, b.y, b.width, b.height, b.r) }, image: function (a) { var b = a.attrs; return bg(b.x, b.y, b.width, b.height) }, text: function (a) { var b = a._getBBox(); return bg(b.x, b.y, b.width, b.height) } }, bj = a.mapPath = function (a, b) { if (!b) return a; var c, d, e, f, g, h, i; a = bR(a); for (e = 0, g = a.length; e < g; e++) { i = a[e]; for (f = 1, h = i.length; f < h; f += 2)c = b.x(i[f], i[f + 1]), d = b.y(i[f], i[f + 1]), i[f] = c, i[f + 1] = d } return a }; a._g = h, a.type = h.win.SVGAngle || h.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML"; if (a.type == "VML") { var bk = h.doc.createElement("div"), bl; bk.innerHTML = '<v:shape adj="1"/>', bl = bk.firstChild, bl.style.behavior = "url(#default#VML)"; if (!bl || typeof bl.adj != "object") return a.type = p; bk = null } a.svg = !(a.vml = a.type == "VML"), a._Paper = j, a.fn = k = j.prototype = a.prototype, a._id = 0, a._oid = 0, a.is = function (a, b) { b = v.call(b); if (b == "finite") return !M[g](+a); if (b == "array") return a instanceof Array; return b == "null" && a === null || b == typeof a && a !== null || b == "object" && a === Object(a) || b == "array" && Array.isArray && Array.isArray(a) || H.call(a).slice(8, -1).toLowerCase() == b }, a.angle = function (b, c, d, e, f, g) { if (f == null) { var h = b - d, i = c - e; if (!h && !i) return 0; return (180 + w.atan2(-i, -h) * 180 / B + 360) % 360 } return a.angle(b, c, f, g) - a.angle(d, e, f, g) }, a.rad = function (a) { return a % 360 * B / 180 }, a.deg = function (a) { return a * 180 / B % 360 }, a.snapTo = function (b, c, d) { d = a.is(d, "finite") ? d : 10; if (a.is(b, E)) { var e = b.length; while (e--) if (z(b[e] - c) <= d) return b[e] } else { b = +b; var f = c % b; if (f < d) return c - f; if (f > b - d) return c - f + b } return c }; var bn = a.createUUID = function (a, b) { return function () { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a, b).toUpperCase() } }(/[xy]/g, function (a) { var b = w.random() * 16 | 0, c = a == "x" ? b : b & 3 | 8; return c.toString(16) }); a.setWindow = function (b) { eve("raphael.setWindow", a, h.win, b), h.win = b, h.doc = h.win.document, a._engine.initWin && a._engine.initWin(h.win) }; var bo = function (b) { if (a.vml) { var c = /^\s+|\s+$/g, d; try { var e = new ActiveXObject("htmlfile"); e.write("<body>"), e.close(), d = e.body } catch (f) { d = createPopup().document.body } var g = d.createTextRange(); bo = bv(function (a) { try { d.style.color = r(a).replace(c, p); var b = g.queryCommandValue("ForeColor"); b = (b & 255) << 16 | b & 65280 | (b & 16711680) >>> 16; return "#" + ("000000" + b.toString(16)).slice(-6) } catch (e) { return "none" } }) } else { var i = h.doc.createElement("i"); i.title = "RaphaÃ«l Colour Picker", i.style.display = "none", h.doc.body.appendChild(i), bo = bv(function (a) { i.style.color = a; return h.doc.defaultView.getComputedStyle(i, p).getPropertyValue("color") }) } return bo(b) }, bp = function () { return "hsb(" + [this.h, this.s, this.b] + ")" }, bq = function () { return "hsl(" + [this.h, this.s, this.l] + ")" }, br = function () { return this.hex }, bs = function (b, c, d) { c == null && a.is(b, "object") && "r" in b && "g" in b && "b" in b && (d = b.b, c = b.g, b = b.r); if (c == null && a.is(b, D)) { var e = a.getRGB(b); b = e.r, c = e.g, d = e.b } if (b > 1 || c > 1 || d > 1) b /= 255, c /= 255, d /= 255; return [b, c, d] }, bt = function (b, c, d, e) { b *= 255, c *= 255, d *= 255; var f = { r: b, g: c, b: d, hex: a.rgb(b, c, d), toString: br }; a.is(e, "finite") && (f.opacity = e); return f }; a.color = function (b) { var c; a.is(b, "object") && "h" in b && "s" in b && "b" in b ? (c = a.hsb2rgb(b), b.r = c.r, b.g = c.g, b.b = c.b, b.hex = c.hex) : a.is(b, "object") && "h" in b && "s" in b && "l" in b ? (c = a.hsl2rgb(b), b.r = c.r, b.g = c.g, b.b = c.b, b.hex = c.hex) : (a.is(b, "string") && (b = a.getRGB(b)), a.is(b, "object") && "r" in b && "g" in b && "b" in b ? (c = a.rgb2hsl(b), b.h = c.h, b.s = c.s, b.l = c.l, c = a.rgb2hsb(b), b.v = c.b) : (b = { hex: "none" }, b.r = b.g = b.b = b.h = b.s = b.v = b.l = -1)), b.toString = br; return b }, a.hsb2rgb = function (a, b, c, d) { this.is(a, "object") && "h" in a && "s" in a && "b" in a && (c = a.b, b = a.s, a = a.h, d = a.o), a *= 360; var e, f, g, h, i; a = a % 360 / 60, i = c * b, h = i * (1 - z(a % 2 - 1)), e = f = g = c - i, a = ~~a, e += [i, h, 0, 0, h, i][a], f += [h, i, i, h, 0, 0][a], g += [0, 0, h, i, i, h][a]; return bt(e, f, g, d) }, a.hsl2rgb = function (a, b, c, d) { this.is(a, "object") && "h" in a && "s" in a && "l" in a && (c = a.l, b = a.s, a = a.h); if (a > 1 || b > 1 || c > 1) a /= 360, b /= 100, c /= 100; a *= 360; var e, f, g, h, i; a = a % 360 / 60, i = 2 * b * (c < .5 ? c : 1 - c), h = i * (1 - z(a % 2 - 1)), e = f = g = c - i / 2, a = ~~a, e += [i, h, 0, 0, h, i][a], f += [h, i, i, h, 0, 0][a], g += [0, 0, h, i, i, h][a]; return bt(e, f, g, d) }, a.rgb2hsb = function (a, b, c) { c = bs(a, b, c), a = c[0], b = c[1], c = c[2]; var d, e, f, g; f = x(a, b, c), g = f - y(a, b, c), d = g == 0 ? null : f == a ? (b - c) / g : f == b ? (c - a) / g + 2 : (a - b) / g + 4, d = (d + 360) % 6 * 60 / 360, e = g == 0 ? 0 : g / f; return { h: d, s: e, b: f, toString: bp } }, a.rgb2hsl = function (a, b, c) { c = bs(a, b, c), a = c[0], b = c[1], c = c[2]; var d, e, f, g, h, i; g = x(a, b, c), h = y(a, b, c), i = g - h, d = i == 0 ? null : g == a ? (b - c) / i : g == b ? (c - a) / i + 2 : (a - b) / i + 4, d = (d + 360) % 6 * 60 / 360, f = (g + h) / 2, e = i == 0 ? 0 : f < .5 ? i / (2 * f) : i / (2 - 2 * f); return { h: d, s: e, l: f, toString: bq } }, a._path2string = function () { return this.join(",").replace(Y, "$1") }; var bw = a._preload = function (a, b) { var c = h.doc.createElement("img"); c.style.cssText = "position:absolute;left:-9999em;top:-9999em", c.onload = function () { b.call(this), this.onload = null, h.doc.body.removeChild(this) }, c.onerror = function () { h.doc.body.removeChild(this) }, h.doc.body.appendChild(c), c.src = a }; a.getRGB = bv(function (b) { if (!b || !!((b = r(b)).indexOf("-") + 1)) return { r: -1, g: -1, b: -1, hex: "none", error: 1, toString: bx }; if (b == "none") return { r: -1, g: -1, b: -1, hex: "none", toString: bx }; !X[g](b.toLowerCase().substring(0, 2)) && b.charAt() != "#" && (b = bo(b)); var c, d, e, f, h, i, j, k = b.match(L); if (k) { k[2] && (f = R(k[2].substring(5), 16), e = R(k[2].substring(3, 5), 16), d = R(k[2].substring(1, 3), 16)), k[3] && (f = R((i = k[3].charAt(3)) + i, 16), e = R((i = k[3].charAt(2)) + i, 16), d = R((i = k[3].charAt(1)) + i, 16)), k[4] && (j = k[4][s](W), d = Q(j[0]), j[0].slice(-1) == "%" && (d *= 2.55), e = Q(j[1]), j[1].slice(-1) == "%" && (e *= 2.55), f = Q(j[2]), j[2].slice(-1) == "%" && (f *= 2.55), k[1].toLowerCase().slice(0, 4) == "rgba" && (h = Q(j[3])), j[3] && j[3].slice(-1) == "%" && (h /= 100)); if (k[5]) { j = k[5][s](W), d = Q(j[0]), j[0].slice(-1) == "%" && (d *= 2.55), e = Q(j[1]), j[1].slice(-1) == "%" && (e *= 2.55), f = Q(j[2]), j[2].slice(-1) == "%" && (f *= 2.55), (j[0].slice(-3) == "deg" || j[0].slice(-1) == "Â°") && (d /= 360), k[1].toLowerCase().slice(0, 4) == "hsba" && (h = Q(j[3])), j[3] && j[3].slice(-1) == "%" && (h /= 100); return a.hsb2rgb(d, e, f, h) } if (k[6]) { j = k[6][s](W), d = Q(j[0]), j[0].slice(-1) == "%" && (d *= 2.55), e = Q(j[1]), j[1].slice(-1) == "%" && (e *= 2.55), f = Q(j[2]), j[2].slice(-1) == "%" && (f *= 2.55), (j[0].slice(-3) == "deg" || j[0].slice(-1) == "Â°") && (d /= 360), k[1].toLowerCase().slice(0, 4) == "hsla" && (h = Q(j[3])), j[3] && j[3].slice(-1) == "%" && (h /= 100); return a.hsl2rgb(d, e, f, h) } k = { r: d, g: e, b: f, toString: bx }, k.hex = "#" + (16777216 | f | e << 8 | d << 16).toString(16).slice(1), a.is(h, "finite") && (k.opacity = h); return k } return { r: -1, g: -1, b: -1, hex: "none", error: 1, toString: bx } }, a), a.hsb = bv(function (b, c, d) { return a.hsb2rgb(b, c, d).hex }), a.hsl = bv(function (b, c, d) { return a.hsl2rgb(b, c, d).hex }), a.rgb = bv(function (a, b, c) { return "#" + (16777216 | c | b << 8 | a << 16).toString(16).slice(1) }), a.getColor = function (a) { var b = this.getColor.start = this.getColor.start || { h: 0, s: 1, b: a || .75 }, c = this.hsb2rgb(b.h, b.s, b.b); b.h += .075, b.h > 1 && (b.h = 0, b.s -= .2, b.s <= 0 && (this.getColor.start = { h: 0, s: 1, b: b.b })); return c.hex }, a.getColor.reset = function () { delete this.start }, a.parsePathString = function (b) { if (!b) return null; var c = bz(b); if (c.arr) return bJ(c.arr); var d = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 }, e = []; a.is(b, E) && a.is(b[0], E) && (e = bJ(b)), e.length || r(b).replace(Z, function (a, b, c) { var f = [], g = b.toLowerCase(); c.replace(_, function (a, b) { b && f.push(+b) }), g == "m" && f.length > 2 && (e.push([b][n](f.splice(0, 2))), g = "l", b = b == "m" ? "l" : "L"); if (g == "r") e.push([b][n](f)); else while (f.length >= d[g]) { e.push([b][n](f.splice(0, d[g]))); if (!d[g]) break } }), e.toString = a._path2string, c.arr = bJ(e); return e }, a.parseTransformString = bv(function (b) { if (!b) return null; var c = { r: 3, s: 4, t: 2, m: 6 }, d = []; a.is(b, E) && a.is(b[0], E) && (d = bJ(b)), d.length || r(b).replace($, function (a, b, c) { var e = [], f = v.call(b); c.replace(_, function (a, b) { b && e.push(+b) }), d.push([b][n](e)) }), d.toString = a._path2string; return d }); var bz = function (a) { var b = bz.ps = bz.ps || {}; b[a] ? b[a].sleep = 100 : b[a] = { sleep: 100 }, setTimeout(function () { for (var c in b) b[g](c) && c != a && (b[c].sleep--, !b[c].sleep && delete b[c]) }); return b[a] }; a.findDotsAtSegment = function (a, b, c, d, e, f, g, h, i) { var j = 1 - i, k = A(j, 3), l = A(j, 2), m = i * i, n = m * i, o = k * a + l * 3 * i * c + j * 3 * i * i * e + n * g, p = k * b + l * 3 * i * d + j * 3 * i * i * f + n * h, q = a + 2 * i * (c - a) + m * (e - 2 * c + a), r = b + 2 * i * (d - b) + m * (f - 2 * d + b), s = c + 2 * i * (e - c) + m * (g - 2 * e + c), t = d + 2 * i * (f - d) + m * (h - 2 * f + d), u = j * a + i * c, v = j * b + i * d, x = j * e + i * g, y = j * f + i * h, z = 90 - w.atan2(q - s, r - t) * 180 / B; (q > s || r < t) && (z += 180); return { x: o, y: p, m: { x: q, y: r }, n: { x: s, y: t }, start: { x: u, y: v }, end: { x: x, y: y }, alpha: z } }, a.bezierBBox = function (b, c, d, e, f, g, h, i) { a.is(b, "array") || (b = [b, c, d, e, f, g, h, i]); var j = bQ.apply(null, b); return { x: j.min.x, y: j.min.y, x2: j.max.x, y2: j.max.y, width: j.max.x - j.min.x, height: j.max.y - j.min.y } }, a.isPointInsideBBox = function (a, b, c) { return b >= a.x && b <= a.x2 && c >= a.y && c <= a.y2 }, a.isBBoxIntersect = function (b, c) { var d = a.isPointInsideBBox; return d(c, b.x, b.y) || d(c, b.x2, b.y) || d(c, b.x, b.y2) || d(c, b.x2, b.y2) || d(b, c.x, c.y) || d(b, c.x2, c.y) || d(b, c.x, c.y2) || d(b, c.x2, c.y2) || (b.x < c.x2 && b.x > c.x || c.x < b.x2 && c.x > b.x) && (b.y < c.y2 && b.y > c.y || c.y < b.y2 && c.y > b.y) }, a.pathIntersection = function (a, b) { return bH(a, b) }, a.pathIntersectionNumber = function (a, b) { return bH(a, b, 1) }, a.isPointInsidePath = function (b, c, d) { var e = a.pathBBox(b); return a.isPointInsideBBox(e, c, d) && bH(b, [["M", c, d], ["H", e.x2 + 10]], 1) % 2 == 1 }, a._removedFactory = function (a) { return function () { eve("raphael.log", null, "RaphaÃ«l: you are calling to method â€œ" + a + "â€ of removed object", a) } }; var bI = a.pathBBox = function (a) { var b = bz(a); if (b.bbox) return b.bbox; if (!a) return { x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0 }; a = bR(a); var c = 0, d = 0, e = [], f = [], g; for (var h = 0, i = a.length; h < i; h++) { g = a[h]; if (g[0] == "M") c = g[1], d = g[2], e.push(c), f.push(d); else { var j = bQ(c, d, g[1], g[2], g[3], g[4], g[5], g[6]); e = e[n](j.min.x, j.max.x), f = f[n](j.min.y, j.max.y), c = g[5], d = g[6] } } var k = y[m](0, e), l = y[m](0, f), o = x[m](0, e), p = x[m](0, f), q = { x: k, y: l, x2: o, y2: p, width: o - k, height: p - l }; b.bbox = bm(q); return q }, bJ = function (b) { var c = bm(b); c.toString = a._path2string; return c }, bK = a._pathToRelative = function (b) { var c = bz(b); if (c.rel) return bJ(c.rel); if (!a.is(b, E) || !a.is(b && b[0], E)) b = a.parsePathString(b); var d = [], e = 0, f = 0, g = 0, h = 0, i = 0; b[0][0] == "M" && (e = b[0][1], f = b[0][2], g = e, h = f, i++, d.push(["M", e, f])); for (var j = i, k = b.length; j < k; j++) { var l = d[j] = [], m = b[j]; if (m[0] != v.call(m[0])) { l[0] = v.call(m[0]); switch (l[0]) { case "a": l[1] = m[1], l[2] = m[2], l[3] = m[3], l[4] = m[4], l[5] = m[5], l[6] = +(m[6] - e).toFixed(3), l[7] = +(m[7] - f).toFixed(3); break; case "v": l[1] = +(m[1] - f).toFixed(3); break; case "m": g = m[1], h = m[2]; default: for (var n = 1, o = m.length; n < o; n++)l[n] = +(m[n] - (n % 2 ? e : f)).toFixed(3) } } else { l = d[j] = [], m[0] == "m" && (g = m[1] + e, h = m[2] + f); for (var p = 0, q = m.length; p < q; p++)d[j][p] = m[p] } var r = d[j].length; switch (d[j][0]) { case "z": e = g, f = h; break; case "h": e += +d[j][r - 1]; break; case "v": f += +d[j][r - 1]; break; default: e += +d[j][r - 2], f += +d[j][r - 1] } } d.toString = a._path2string, c.rel = bJ(d); return d }, bL = a._pathToAbsolute = function (b) { var c = bz(b); if (c.abs) return bJ(c.abs); if (!a.is(b, E) || !a.is(b && b[0], E)) b = a.parsePathString(b); if (!b || !b.length) return [["M", 0, 0]]; var d = [], e = 0, f = 0, g = 0, h = 0, i = 0; b[0][0] == "M" && (e = +b[0][1], f = +b[0][2], g = e, h = f, i++, d[0] = ["M", e, f]); var j = b.length == 3 && b[0][0] == "M" && b[1][0].toUpperCase() == "R" && b[2][0].toUpperCase() == "Z"; for (var k, l, m = i, o = b.length; m < o; m++) { d.push(k = []), l = b[m]; if (l[0] != S.call(l[0])) { k[0] = S.call(l[0]); switch (k[0]) { case "A": k[1] = l[1], k[2] = l[2], k[3] = l[3], k[4] = l[4], k[5] = l[5], k[6] = +(l[6] + e), k[7] = +(l[7] + f); break; case "V": k[1] = +l[1] + f; break; case "H": k[1] = +l[1] + e; break; case "R": var p = [e, f][n](l.slice(1)); for (var q = 2, r = p.length; q < r; q++)p[q] = +p[q] + e, p[++q] = +p[q] + f; d.pop(), d = d[n](by(p, j)); break; case "M": g = +l[1] + e, h = +l[2] + f; default: for (q = 1, r = l.length; q < r; q++)k[q] = +l[q] + (q % 2 ? e : f) } } else if (l[0] == "R") p = [e, f][n](l.slice(1)), d.pop(), d = d[n](by(p, j)), k = ["R"][n](l.slice(-2)); else for (var s = 0, t = l.length; s < t; s++)k[s] = l[s]; switch (k[0]) { case "Z": e = g, f = h; break; case "H": e = k[1]; break; case "V": f = k[1]; break; case "M": g = k[k.length - 2], h = k[k.length - 1]; default: e = k[k.length - 2], f = k[k.length - 1] } } d.toString = a._path2string, c.abs = bJ(d); return d }, bM = function (a, b, c, d) { return [a, b, c, d, c, d] }, bN = function (a, b, c, d, e, f) { var g = 1 / 3, h = 2 / 3; return [g * a + h * c, g * b + h * d, g * e + h * c, g * f + h * d, e, f] }, bO = function (a, b, c, d, e, f, g, h, i, j) { var k = B * 120 / 180, l = B / 180 * (+e || 0), m = [], o, p = bv(function (a, b, c) { var d = a * w.cos(c) - b * w.sin(c), e = a * w.sin(c) + b * w.cos(c); return { x: d, y: e } }); if (!j) { o = p(a, b, -l), a = o.x, b = o.y, o = p(h, i, -l), h = o.x, i = o.y; var q = w.cos(B / 180 * e), r = w.sin(B / 180 * e), t = (a - h) / 2, u = (b - i) / 2, v = t * t / (c * c) + u * u / (d * d); v > 1 && (v = w.sqrt(v), c = v * c, d = v * d); var x = c * c, y = d * d, A = (f == g ? -1 : 1) * w.sqrt(z((x * y - x * u * u - y * t * t) / (x * u * u + y * t * t))), C = A * c * u / d + (a + h) / 2, D = A * -d * t / c + (b + i) / 2, E = w.asin(((b - D) / d).toFixed(9)), F = w.asin(((i - D) / d).toFixed(9)); E = a < C ? B - E : E, F = h < C ? B - F : F, E < 0 && (E = B * 2 + E), F < 0 && (F = B * 2 + F), g && E > F && (E = E - B * 2), !g && F > E && (F = F - B * 2) } else E = j[0], F = j[1], C = j[2], D = j[3]; var G = F - E; if (z(G) > k) { var H = F, I = h, J = i; F = E + k * (g && F > E ? 1 : -1), h = C + c * w.cos(F), i = D + d * w.sin(F), m = bO(h, i, c, d, e, 0, g, I, J, [F, H, C, D]) } G = F - E; var K = w.cos(E), L = w.sin(E), M = w.cos(F), N = w.sin(F), O = w.tan(G / 4), P = 4 / 3 * c * O, Q = 4 / 3 * d * O, R = [a, b], S = [a + P * L, b - Q * K], T = [h + P * N, i - Q * M], U = [h, i]; S[0] = 2 * R[0] - S[0], S[1] = 2 * R[1] - S[1]; if (j) return [S, T, U][n](m); m = [S, T, U][n](m).join()[s](","); var V = []; for (var W = 0, X = m.length; W < X; W++)V[W] = W % 2 ? p(m[W - 1], m[W], l).y : p(m[W], m[W + 1], l).x; return V }, bP = function (a, b, c, d, e, f, g, h, i) { var j = 1 - i; return { x: A(j, 3) * a + A(j, 2) * 3 * i * c + j * 3 * i * i * e + A(i, 3) * g, y: A(j, 3) * b + A(j, 2) * 3 * i * d + j * 3 * i * i * f + A(i, 3) * h } }, bQ = bv(function (a, b, c, d, e, f, g, h) { var i = e - 2 * c + a - (g - 2 * e + c), j = 2 * (c - a) - 2 * (e - c), k = a - c, l = (-j + w.sqrt(j * j - 4 * i * k)) / 2 / i, n = (-j - w.sqrt(j * j - 4 * i * k)) / 2 / i, o = [b, h], p = [a, g], q; z(l) > "1e12" && (l = .5), z(n) > "1e12" && (n = .5), l > 0 && l < 1 && (q = bP(a, b, c, d, e, f, g, h, l), p.push(q.x), o.push(q.y)), n > 0 && n < 1 && (q = bP(a, b, c, d, e, f, g, h, n), p.push(q.x), o.push(q.y)), i = f - 2 * d + b - (h - 2 * f + d), j = 2 * (d - b) - 2 * (f - d), k = b - d, l = (-j + w.sqrt(j * j - 4 * i * k)) / 2 / i, n = (-j - w.sqrt(j * j - 4 * i * k)) / 2 / i, z(l) > "1e12" && (l = .5), z(n) > "1e12" && (n = .5), l > 0 && l < 1 && (q = bP(a, b, c, d, e, f, g, h, l), p.push(q.x), o.push(q.y)), n > 0 && n < 1 && (q = bP(a, b, c, d, e, f, g, h, n), p.push(q.x), o.push(q.y)); return { min: { x: y[m](0, p), y: y[m](0, o) }, max: { x: x[m](0, p), y: x[m](0, o) } } }), bR = a._path2curve = bv(function (a, b) { var c = !b && bz(a); if (!b && c.curve) return bJ(c.curve); var d = bL(a), e = b && bL(b), f = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null }, g = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null }, h = function (a, b) { var c, d; if (!a) return ["C", b.x, b.y, b.x, b.y, b.x, b.y]; !(a[0] in { T: 1, Q: 1 }) && (b.qx = b.qy = null); switch (a[0]) { case "M": b.X = a[1], b.Y = a[2]; break; case "A": a = ["C"][n](bO[m](0, [b.x, b.y][n](a.slice(1)))); break; case "S": c = b.x + (b.x - (b.bx || b.x)), d = b.y + (b.y - (b.by || b.y)), a = ["C", c, d][n](a.slice(1)); break; case "T": b.qx = b.x + (b.x - (b.qx || b.x)), b.qy = b.y + (b.y - (b.qy || b.y)), a = ["C"][n](bN(b.x, b.y, b.qx, b.qy, a[1], a[2])); break; case "Q": b.qx = a[1], b.qy = a[2], a = ["C"][n](bN(b.x, b.y, a[1], a[2], a[3], a[4])); break; case "L": a = ["C"][n](bM(b.x, b.y, a[1], a[2])); break; case "H": a = ["C"][n](bM(b.x, b.y, a[1], b.y)); break; case "V": a = ["C"][n](bM(b.x, b.y, b.x, a[1])); break; case "Z": a = ["C"][n](bM(b.x, b.y, b.X, b.Y)) }return a }, i = function (a, b) { if (a[b].length > 7) { a[b].shift(); var c = a[b]; while (c.length) a.splice(b++, 0, ["C"][n](c.splice(0, 6))); a.splice(b, 1), l = x(d.length, e && e.length || 0) } }, j = function (a, b, c, f, g) { a && b && a[g][0] == "M" && b[g][0] != "M" && (b.splice(g, 0, ["M", f.x, f.y]), c.bx = 0, c.by = 0, c.x = a[g][1], c.y = a[g][2], l = x(d.length, e && e.length || 0)) }; for (var k = 0, l = x(d.length, e && e.length || 0); k < l; k++) { d[k] = h(d[k], f), i(d, k), e && (e[k] = h(e[k], g)), e && i(e, k), j(d, e, f, g, k), j(e, d, g, f, k); var o = d[k], p = e && e[k], q = o.length, r = e && p.length; f.x = o[q - 2], f.y = o[q - 1], f.bx = Q(o[q - 4]) || f.x, f.by = Q(o[q - 3]) || f.y, g.bx = e && (Q(p[r - 4]) || g.x), g.by = e && (Q(p[r - 3]) || g.y), g.x = e && p[r - 2], g.y = e && p[r - 1] } e || (c.curve = bJ(d)); return e ? [d, e] : d }, null, bJ), bS = a._parseDots = bv(function (b) { var c = []; for (var d = 0, e = b.length; d < e; d++) { var f = {}, g = b[d].match(/^([^:]*):?([\d\.]*)/); f.color = a.getRGB(g[1]); if (f.color.error) return null; f.color = f.color.hex, g[2] && (f.offset = g[2] + "%"), c.push(f) } for (d = 1, e = c.length - 1; d < e; d++)if (!c[d].offset) { var h = Q(c[d - 1].offset || 0), i = 0; for (var j = d + 1; j < e; j++)if (c[j].offset) { i = c[j].offset; break } i || (i = 100, j = e), i = Q(i); var k = (i - h) / (j - d + 1); for (; d < j; d++)h += k, c[d].offset = h + "%" } return c }), bT = a._tear = function (a, b) { a == b.top && (b.top = a.prev), a == b.bottom && (b.bottom = a.next), a.next && (a.next.prev = a.prev), a.prev && (a.prev.next = a.next) }, bU = a._tofront = function (a, b) { b.top !== a && (bT(a, b), a.next = null, a.prev = b.top, b.top.next = a, b.top = a) }, bV = a._toback = function (a, b) { b.bottom !== a && (bT(a, b), a.next = b.bottom, a.prev = null, b.bottom.prev = a, b.bottom = a) }, bW = a._insertafter = function (a, b, c) { bT(a, c), b == c.top && (c.top = a), b.next && (b.next.prev = a), a.next = b.next, a.prev = b, b.next = a }, bX = a._insertbefore = function (a, b, c) { bT(a, c), b == c.bottom && (c.bottom = a), b.prev && (b.prev.next = a), a.prev = b.prev, b.prev = a, a.next = b }, bY = a.toMatrix = function (a, b) { var c = bI(a), d = { _: { transform: p }, getBBox: function () { return c } }; b$(d, b); return d.matrix }, bZ = a.transformPath = function (a, b) { return bj(a, bY(a, b)) }, b$ = a._extractTransform = function (b, c) { if (c == null) return b._.transform; c = r(c).replace(/\.{3}|\u2026/g, b._.transform || p); var d = a.parseTransformString(c), e = 0, f = 0, g = 0, h = 1, i = 1, j = b._, k = new cb; j.transform = d || []; if (d) for (var l = 0, m = d.length; l < m; l++) { var n = d[l], o = n.length, q = r(n[0]).toLowerCase(), s = n[0] != q, t = s ? k.invert() : 0, u, v, w, x, y; q == "t" && o == 3 ? s ? (u = t.x(0, 0), v = t.y(0, 0), w = t.x(n[1], n[2]), x = t.y(n[1], n[2]), k.translate(w - u, x - v)) : k.translate(n[1], n[2]) : q == "r" ? o == 2 ? (y = y || b.getBBox(1), k.rotate(n[1], y.x + y.width / 2, y.y + y.height / 2), e += n[1]) : o == 4 && (s ? (w = t.x(n[2], n[3]), x = t.y(n[2], n[3]), k.rotate(n[1], w, x)) : k.rotate(n[1], n[2], n[3]), e += n[1]) : q == "s" ? o == 2 || o == 3 ? (y = y || b.getBBox(1), k.scale(n[1], n[o - 1], y.x + y.width / 2, y.y + y.height / 2), h *= n[1], i *= n[o - 1]) : o == 5 && (s ? (w = t.x(n[3], n[4]), x = t.y(n[3], n[4]), k.scale(n[1], n[2], w, x)) : k.scale(n[1], n[2], n[3], n[4]), h *= n[1], i *= n[2]) : q == "m" && o == 7 && k.add(n[1], n[2], n[3], n[4], n[5], n[6]), j.dirtyT = 1, b.matrix = k } b.matrix = k, j.sx = h, j.sy = i, j.deg = e, j.dx = f = k.e, j.dy = g = k.f, h == 1 && i == 1 && !e && j.bbox ? (j.bbox.x += +f, j.bbox.y += +g) : j.dirtyT = 1 }, b_ = function (a) { var b = a[0]; switch (b.toLowerCase()) { case "t": return [b, 0, 0]; case "m": return [b, 1, 0, 0, 1, 0, 0]; case "r": return a.length == 4 ? [b, 0, a[2], a[3]] : [b, 0]; case "s": return a.length == 5 ? [b, 1, 1, a[3], a[4]] : a.length == 3 ? [b, 1, 1] : [b, 1] } }, ca = a._equaliseTransform = function (b, c) { c = r(c).replace(/\.{3}|\u2026/g, b), b = a.parseTransformString(b) || [], c = a.parseTransformString(c) || []; var d = x(b.length, c.length), e = [], f = [], g = 0, h, i, j, k; for (; g < d; g++) { j = b[g] || b_(c[g]), k = c[g] || b_(j); if (j[0] != k[0] || j[0].toLowerCase() == "r" && (j[2] != k[2] || j[3] != k[3]) || j[0].toLowerCase() == "s" && (j[3] != k[3] || j[4] != k[4])) return; e[g] = [], f[g] = []; for (h = 0, i = x(j.length, k.length); h < i; h++)h in j && (e[g][h] = j[h]), h in k && (f[g][h] = k[h]) } return { from: e, to: f } }; a._getContainer = function (b, c, d, e) { var f; f = e == null && !a.is(b, "object") ? h.doc.getElementById(b) : b; if (f != null) { if (f.tagName) return c == null ? { container: f, width: f.style.pixelWidth || f.offsetWidth, height: f.style.pixelHeight || f.offsetHeight } : { container: f, width: c, height: d }; return { container: 1, x: b, y: c, width: d, height: e } } }, a.pathToRelative = bK, a._engine = {}, a.path2curve = bR, a.matrix = function (a, b, c, d, e, f) { return new cb(a, b, c, d, e, f) }, function (b) { function d(a) { var b = w.sqrt(c(a)); a[0] && (a[0] /= b), a[1] && (a[1] /= b) } function c(a) { return a[0] * a[0] + a[1] * a[1] } b.add = function (a, b, c, d, e, f) { var g = [[], [], []], h = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]], i = [[a, c, e], [b, d, f], [0, 0, 1]], j, k, l, m; a && a instanceof cb && (i = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]]); for (j = 0; j < 3; j++)for (k = 0; k < 3; k++) { m = 0; for (l = 0; l < 3; l++)m += h[j][l] * i[l][k]; g[j][k] = m } this.a = g[0][0], this.b = g[1][0], this.c = g[0][1], this.d = g[1][1], this.e = g[0][2], this.f = g[1][2] }, b.invert = function () { var a = this, b = a.a * a.d - a.b * a.c; return new cb(a.d / b, -a.b / b, -a.c / b, a.a / b, (a.c * a.f - a.d * a.e) / b, (a.b * a.e - a.a * a.f) / b) }, b.clone = function () { return new cb(this.a, this.b, this.c, this.d, this.e, this.f) }, b.translate = function (a, b) { this.add(1, 0, 0, 1, a, b) }, b.scale = function (a, b, c, d) { b == null && (b = a), (c || d) && this.add(1, 0, 0, 1, c, d), this.add(a, 0, 0, b, 0, 0), (c || d) && this.add(1, 0, 0, 1, -c, -d) }, b.rotate = function (b, c, d) { b = a.rad(b), c = c || 0, d = d || 0; var e = +w.cos(b).toFixed(9), f = +w.sin(b).toFixed(9); this.add(e, f, -f, e, c, d), this.add(1, 0, 0, 1, -c, -d) }, b.x = function (a, b) { return a * this.a + b * this.c + this.e }, b.y = function (a, b) { return a * this.b + b * this.d + this.f }, b.get = function (a) { return +this[r.fromCharCode(97 + a)].toFixed(4) }, b.toString = function () { return a.svg ? "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" : [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join() }, b.toFilter = function () { return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) + ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) + ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')" }, b.offset = function () { return [this.e.toFixed(4), this.f.toFixed(4)] }, b.split = function () { var b = {}; b.dx = this.e, b.dy = this.f; var e = [[this.a, this.c], [this.b, this.d]]; b.scalex = w.sqrt(c(e[0])), d(e[0]), b.shear = e[0][0] * e[1][0] + e[0][1] * e[1][1], e[1] = [e[1][0] - e[0][0] * b.shear, e[1][1] - e[0][1] * b.shear], b.scaley = w.sqrt(c(e[1])), d(e[1]), b.shear /= b.scaley; var f = -e[0][1], g = e[1][1]; g < 0 ? (b.rotate = a.deg(w.acos(g)), f < 0 && (b.rotate = 360 - b.rotate)) : b.rotate = a.deg(w.asin(f)), b.isSimple = !+b.shear.toFixed(9) && (b.scalex.toFixed(9) == b.scaley.toFixed(9) || !b.rotate), b.isSuperSimple = !+b.shear.toFixed(9) && b.scalex.toFixed(9) == b.scaley.toFixed(9) && !b.rotate, b.noRotation = !+b.shear.toFixed(9) && !b.rotate; return b }, b.toTransformString = function (a) { var b = a || this[s](); if (b.isSimple) { b.scalex = +b.scalex.toFixed(4), b.scaley = +b.scaley.toFixed(4), b.rotate = +b.rotate.toFixed(4); return (b.dx || b.dy ? "t" + [b.dx, b.dy] : p) + (b.scalex != 1 || b.scaley != 1 ? "s" + [b.scalex, b.scaley, 0, 0] : p) + (b.rotate ? "r" + [b.rotate, 0, 0] : p) } return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)] } }(cb.prototype); var cc = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/); navigator.vendor == "Apple Computer, Inc." && (cc && cc[1] < 4 || navigator.platform.slice(0, 2) == "iP") || navigator.vendor == "Google Inc." && cc && cc[1] < 8 ? k.safari = function () { var a = this.rect(-99, -99, this.width + 99, this.height + 99).attr({ stroke: "none" }); setTimeout(function () { a.remove() }) } : k.safari = be; var cd = function () { this.returnValue = !1 }, ce = function () { return this.originalEvent.preventDefault() }, cf = function () { this.cancelBubble = !0 }, cg = function () { return this.originalEvent.stopPropagation() }, ch = function () { if (h.doc.addEventListener) return function (a, b, c, d) { var e = o && u[b] ? u[b] : b, f = function (e) { var f = h.doc.documentElement.scrollTop || h.doc.body.scrollTop, i = h.doc.documentElement.scrollLeft || h.doc.body.scrollLeft, j = e.clientX + i, k = e.clientY + f; if (o && u[g](b)) for (var l = 0, m = e.targetTouches && e.targetTouches.length; l < m; l++)if (e.targetTouches[l].target == a) { var n = e; e = e.targetTouches[l], e.originalEvent = n, e.preventDefault = ce, e.stopPropagation = cg; break } return c.call(d, e, j, k) }; a.addEventListener(e, f, !1); return function () { a.removeEventListener(e, f, !1); return !0 } }; if (h.doc.attachEvent) return function (a, b, c, d) { var e = function (a) { a = a || h.win.event; var b = h.doc.documentElement.scrollTop || h.doc.body.scrollTop, e = h.doc.documentElement.scrollLeft || h.doc.body.scrollLeft, f = a.clientX + e, g = a.clientY + b; a.preventDefault = a.preventDefault || cd, a.stopPropagation = a.stopPropagation || cf; return c.call(d, a, f, g) }; a.attachEvent("on" + b, e); var f = function () { a.detachEvent("on" + b, e); return !0 }; return f } }(), ci = [], cj = function (a) { var b = a.clientX, c = a.clientY, d = h.doc.documentElement.scrollTop || h.doc.body.scrollTop, e = h.doc.documentElement.scrollLeft || h.doc.body.scrollLeft, f, g = ci.length; while (g--) { f = ci[g]; if (o) { var i = a.touches.length, j; while (i--) { j = a.touches[i]; if (j.identifier == f.el._drag.id) { b = j.clientX, c = j.clientY, (a.originalEvent ? a.originalEvent : a).preventDefault(); break } } } else a.preventDefault(); var k = f.el.node, l, m = k.nextSibling, n = k.parentNode, p = k.style.display; h.win.opera && n.removeChild(k), k.style.display = "none", l = f.el.paper.getElementByPoint(b, c), k.style.display = p, h.win.opera && (m ? n.insertBefore(k, m) : n.appendChild(k)), l && eve("raphael.drag.over." + f.el.id, f.el, l), b += e, c += d, eve("raphael.drag.move." + f.el.id, f.move_scope || f.el, b - f.el._drag.x, c - f.el._drag.y, b, c, a) } }, ck = function (b) { a.unmousemove(cj).unmouseup(ck); var c = ci.length, d; while (c--) d = ci[c], d.el._drag = {}, eve("raphael.drag.end." + d.el.id, d.end_scope || d.start_scope || d.move_scope || d.el, b); ci = [] }, cl = a.el = {}; for (var cm = t.length; cm--;)(function (b) { a[b] = cl[b] = function (c, d) { a.is(c, "function") && (this.events = this.events || [], this.events.push({ name: b, f: c, unbind: ch(this.shape || this.node || h.doc, b, c, d || this) })); return this }, a["un" + b] = cl["un" + b] = function (a) { var c = this.events || [], d = c.length; while (d--) if (c[d].name == b && c[d].f == a) { c[d].unbind(), c.splice(d, 1), !c.length && delete this.events; return this } return this } })(t[cm]); cl.data = function (b, c) { var d = bb[this.id] = bb[this.id] || {}; if (arguments.length == 1) { if (a.is(b, "object")) { for (var e in b) b[g](e) && this.data(e, b[e]); return this } eve("raphael.data.get." + this.id, this, d[b], b); return d[b] } d[b] = c, eve("raphael.data.set." + this.id, this, c, b); return this }, cl.removeData = function (a) { a == null ? bb[this.id] = {} : bb[this.id] && delete bb[this.id][a]; return this }, cl.hover = function (a, b, c, d) { return this.mouseover(a, c).mouseout(b, d || c) }, cl.unhover = function (a, b) { return this.unmouseover(a).unmouseout(b) }; var cn = []; cl.drag = function (b, c, d, e, f, g) { function i(i) { (i.originalEvent || i).preventDefault(); var j = h.doc.documentElement.scrollTop || h.doc.body.scrollTop, k = h.doc.documentElement.scrollLeft || h.doc.body.scrollLeft; this._drag.x = i.clientX + k, this._drag.y = i.clientY + j, this._drag.id = i.identifier, !ci.length && a.mousemove(cj).mouseup(ck), ci.push({ el: this, move_scope: e, start_scope: f, end_scope: g }), c && eve.on("raphael.drag.start." + this.id, c), b && eve.on("raphael.drag.move." + this.id, b), d && eve.on("raphael.drag.end." + this.id, d), eve("raphael.drag.start." + this.id, f || e || this, i.clientX + k, i.clientY + j, i) } this._drag = {}, cn.push({ el: this, start: i }), this.mousedown(i); return this }, cl.onDragOver = function (a) { a ? eve.on("raphael.drag.over." + this.id, a) : eve.unbind("raphael.drag.over." + this.id) }, cl.undrag = function () { var b = cn.length; while (b--) cn[b].el == this && (this.unmousedown(cn[b].start), cn.splice(b, 1), eve.unbind("raphael.drag.*." + this.id)); !cn.length && a.unmousemove(cj).unmouseup(ck) }, k.circle = function (b, c, d) { var e = a._engine.circle(this, b || 0, c || 0, d || 0); this.__set__ && this.__set__.push(e); return e }, k.rect = function (b, c, d, e, f) { var g = a._engine.rect(this, b || 0, c || 0, d || 0, e || 0, f || 0); this.__set__ && this.__set__.push(g); return g }, k.ellipse = function (b, c, d, e) { var f = a._engine.ellipse(this, b || 0, c || 0, d || 0, e || 0); this.__set__ && this.__set__.push(f); return f }, k.path = function (b) { b && !a.is(b, D) && !a.is(b[0], E) && (b += p); var c = a._engine.path(a.format[m](a, arguments), this); this.__set__ && this.__set__.push(c); return c }, k.image = function (b, c, d, e, f) { var g = a._engine.image(this, b || "about:blank", c || 0, d || 0, e || 0, f || 0); this.__set__ && this.__set__.push(g); return g }, k.text = function (b, c, d) { var e = a._engine.text(this, b || 0, c || 0, r(d)); this.__set__ && this.__set__.push(e); return e }, k.set = function (b) { !a.is(b, "array") && (b = Array.prototype.splice.call(arguments, 0, arguments.length)); var c = new cG(b); this.__set__ && this.__set__.push(c); return c }, k.setStart = function (a) { this.__set__ = a || this.set() }, k.setFinish = function (a) { var b = this.__set__; delete this.__set__; return b }, k.setSize = function (b, c) { return a._engine.setSize.call(this, b, c) }, k.setViewBox = function (b, c, d, e, f) { return a._engine.setViewBox.call(this, b, c, d, e, f) }, k.top = k.bottom = null, k.raphael = a; var co = function (a) { var b = a.getBoundingClientRect(), c = a.ownerDocument, d = c.body, e = c.documentElement, f = e.clientTop || d.clientTop || 0, g = e.clientLeft || d.clientLeft || 0, i = b.top + (h.win.pageYOffset || e.scrollTop || d.scrollTop) - f, j = b.left + (h.win.pageXOffset || e.scrollLeft || d.scrollLeft) - g; return { y: i, x: j } }; k.getElementByPoint = function (a, b) { var c = this, d = c.canvas, e = h.doc.elementFromPoint(a, b); if (h.win.opera && e.tagName == "svg") { var f = co(d), g = d.createSVGRect(); g.x = a - f.x, g.y = b - f.y, g.width = g.height = 1; var i = d.getIntersectionList(g, null); i.length && (e = i[i.length - 1]) } if (!e) return null; while (e.parentNode && e != d.parentNode && !e.raphael) e = e.parentNode; e == c.canvas.parentNode && (e = d), e = e && e.raphael ? c.getById(e.raphaelid) : null; return e }, k.getById = function (a) { var b = this.bottom; while (b) { if (b.id == a) return b; b = b.next } return null }, k.forEach = function (a, b) { var c = this.bottom; while (c) { if (a.call(b, c) === !1) return this; c = c.next } return this }, k.getElementsByPoint = function (a, b) { var c = this.set(); this.forEach(function (d) { d.isPointInside(a, b) && c.push(d) }); return c }, cl.isPointInside = function (b, c) { var d = this.realPath = this.realPath || bi[this.type](this); return a.isPointInsidePath(d, b, c) }, cl.getBBox = function (a) { if (this.removed) return {}; var b = this._; if (a) { if (b.dirty || !b.bboxwt) this.realPath = bi[this.type](this), b.bboxwt = bI(this.realPath), b.bboxwt.toString = cq, b.dirty = 0; return b.bboxwt } if (b.dirty || b.dirtyT || !b.bbox) { if (b.dirty || !this.realPath) b.bboxwt = 0, this.realPath = bi[this.type](this); b.bbox = bI(bj(this.realPath, this.matrix)), b.bbox.toString = cq, b.dirty = b.dirtyT = 0 } return b.bbox }, cl.clone = function () { if (this.removed) return null; var a = this.paper[this.type]().attr(this.attr()); this.__set__ && this.__set__.push(a); return a }, cl.glow = function (a) { if (this.type == "text") return null; a = a || {}; var b = { width: (a.width || 10) + (+this.attr("stroke-width") || 1), fill: a.fill || !1, opacity: a.opacity || .5, offsetx: a.offsetx || 0, offsety: a.offsety || 0, color: a.color || "#000" }, c = b.width / 2, d = this.paper, e = d.set(), f = this.realPath || bi[this.type](this); f = this.matrix ? bj(f, this.matrix) : f; for (var g = 1; g < c + 1; g++)e.push(d.path(f).attr({ stroke: b.color, fill: b.fill ? b.color : "none", "stroke-linejoin": "round", "stroke-linecap": "round", "stroke-width": +(b.width / c * g).toFixed(3), opacity: +(b.opacity / c).toFixed(3) })); return e.insertBefore(this).translate(b.offsetx, b.offsety) }; var cr = {}, cs = function (b, c, d, e, f, g, h, i, j) { return j == null ? bB(b, c, d, e, f, g, h, i) : a.findDotsAtSegment(b, c, d, e, f, g, h, i, bC(b, c, d, e, f, g, h, i, j)) }, ct = function (b, c) { return function (d, e, f) { d = bR(d); var g, h, i, j, k = "", l = {}, m, n = 0; for (var o = 0, p = d.length; o < p; o++) { i = d[o]; if (i[0] == "M") g = +i[1], h = +i[2]; else { j = cs(g, h, i[1], i[2], i[3], i[4], i[5], i[6]); if (n + j > e) { if (c && !l.start) { m = cs(g, h, i[1], i[2], i[3], i[4], i[5], i[6], e - n), k += ["C" + m.start.x, m.start.y, m.m.x, m.m.y, m.x, m.y]; if (f) return k; l.start = k, k = ["M" + m.x, m.y + "C" + m.n.x, m.n.y, m.end.x, m.end.y, i[5], i[6]].join(), n += j, g = +i[5], h = +i[6]; continue } if (!b && !c) { m = cs(g, h, i[1], i[2], i[3], i[4], i[5], i[6], e - n); return { x: m.x, y: m.y, alpha: m.alpha } } } n += j, g = +i[5], h = +i[6] } k += i.shift() + i } l.end = k, m = b ? n : c ? l : a.findDotsAtSegment(g, h, i[0], i[1], i[2], i[3], i[4], i[5], 1), m.alpha && (m = { x: m.x, y: m.y, alpha: m.alpha }); return m } }, cu = ct(1), cv = ct(), cw = ct(0, 1); a.getTotalLength = cu, a.getPointAtLength = cv, a.getSubpath = function (a, b, c) { if (this.getTotalLength(a) - c < 1e-6) return cw(a, b).end; var d = cw(a, c, 1); return b ? cw(d, b).end : d }, cl.getTotalLength = function () { if (this.type == "path") { if (this.node.getTotalLength) return this.node.getTotalLength(); return cu(this.attrs.path) } }, cl.getPointAtLength = function (a) { if (this.type == "path") return cv(this.attrs.path, a) }, cl.getSubpath = function (b, c) { if (this.type == "path") return a.getSubpath(this.attrs.path, b, c) }; var cx = a.easing_formulas = { linear: function (a) { return a }, "<": function (a) { return A(a, 1.7) }, ">": function (a) { return A(a, .48) }, "<>": function (a) { var b = .48 - a / 1.04, c = w.sqrt(.1734 + b * b), d = c - b, e = A(z(d), 1 / 3) * (d < 0 ? -1 : 1), f = -c - b, g = A(z(f), 1 / 3) * (f < 0 ? -1 : 1), h = e + g + .5; return (1 - h) * 3 * h * h + h * h * h }, backIn: function (a) { var b = 1.70158; return a * a * ((b + 1) * a - b) }, backOut: function (a) { a = a - 1; var b = 1.70158; return a * a * ((b + 1) * a + b) + 1 }, elastic: function (a) { if (a == !!a) return a; return A(2, -10 * a) * w.sin((a - .075) * 2 * B / .3) + 1 }, bounce: function (a) { var b = 7.5625, c = 2.75, d; a < 1 / c ? d = b * a * a : a < 2 / c ? (a -= 1.5 / c, d = b * a * a + .75) : a < 2.5 / c ? (a -= 2.25 / c, d = b * a * a + .9375) : (a -= 2.625 / c, d = b * a * a + .984375); return d } }; cx.easeIn = cx["ease-in"] = cx["<"], cx.easeOut = cx["ease-out"] = cx[">"], cx.easeInOut = cx["ease-in-out"] = cx["<>"], cx["back-in"] = cx.backIn, cx["back-out"] = cx.backOut; var cy = [], cz = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (a) { setTimeout(a, 16) }, cA = function () { var b = +(new Date), c = 0; for (; c < cy.length; c++) { var d = cy[c]; if (d.el.removed || d.paused) continue; var e = b - d.start, f = d.ms, h = d.easing, i = d.from, j = d.diff, k = d.to, l = d.t, m = d.el, o = {}, p, r = {}, s; d.initstatus ? (e = (d.initstatus * d.anim.top - d.prev) / (d.percent - d.prev) * f, d.status = d.initstatus, delete d.initstatus, d.stop && cy.splice(c--, 1)) : d.status = (d.prev + (d.percent - d.prev) * (e / f)) / d.anim.top; if (e < 0) continue; if (e < f) { var t = h(e / f); for (var u in i) if (i[g](u)) { switch (U[u]) { case C: p = +i[u] + t * f * j[u]; break; case "colour": p = "rgb(" + [cB(O(i[u].r + t * f * j[u].r)), cB(O(i[u].g + t * f * j[u].g)), cB(O(i[u].b + t * f * j[u].b))].join(",") + ")"; break; case "path": p = []; for (var v = 0, w = i[u].length; v < w; v++) { p[v] = [i[u][v][0]]; for (var x = 1, y = i[u][v].length; x < y; x++)p[v][x] = +i[u][v][x] + t * f * j[u][v][x]; p[v] = p[v].join(q) } p = p.join(q); break; case "transform": if (j[u].real) { p = []; for (v = 0, w = i[u].length; v < w; v++) { p[v] = [i[u][v][0]]; for (x = 1, y = i[u][v].length; x < y; x++)p[v][x] = i[u][v][x] + t * f * j[u][v][x] } } else { var z = function (a) { return +i[u][a] + t * f * j[u][a] }; p = [["m", z(0), z(1), z(2), z(3), z(4), z(5)]] } break; case "csv": if (u == "clip-rect") { p = [], v = 4; while (v--) p[v] = +i[u][v] + t * f * j[u][v] } break; default: var A = [][n](i[u]); p = [], v = m.paper.customAttributes[u].length; while (v--) p[v] = +A[v] + t * f * j[u][v] }o[u] = p } m.attr(o), function (a, b, c) { setTimeout(function () { eve("raphael.anim.frame." + a, b, c) }) }(m.id, m, d.anim) } else { (function (b, c, d) { setTimeout(function () { eve("raphael.anim.frame." + c.id, c, d), eve("raphael.anim.finish." + c.id, c, d), a.is(b, "function") && b.call(c) }) })(d.callback, m, d.anim), m.attr(k), cy.splice(c--, 1); if (d.repeat > 1 && !d.next) { for (s in k) k[g](s) && (r[s] = d.totalOrigin[s]); d.el.attr(r), cE(d.anim, d.el, d.anim.percents[0], null, d.totalOrigin, d.repeat - 1) } d.next && !d.stop && cE(d.anim, d.el, d.next, null, d.totalOrigin, d.repeat) } } a.svg && m && m.paper && m.paper.safari(), cy.length && cz(cA) }, cB = function (a) { return a > 255 ? 255 : a < 0 ? 0 : a }; cl.animateWith = function (b, c, d, e, f, g) { var h = this; if (h.removed) { g && g.call(h); return h } var i = d instanceof cD ? d : a.animation(d, e, f, g), j, k; cE(i, h, i.percents[0], null, h.attr()); for (var l = 0, m = cy.length; l < m; l++)if (cy[l].anim == c && cy[l].el == b) { cy[m - 1].start = cy[l].start; break } return h }, cl.onAnimation = function (a) { a ? eve.on("raphael.anim.frame." + this.id, a) : eve.unbind("raphael.anim.frame." + this.id); return this }, cD.prototype.delay = function (a) { var b = new cD(this.anim, this.ms); b.times = this.times, b.del = +a || 0; return b }, cD.prototype.repeat = function (a) { var b = new cD(this.anim, this.ms); b.del = this.del, b.times = w.floor(x(a, 0)) || 1; return b }, a.animation = function (b, c, d, e) { if (b instanceof cD) return b; if (a.is(d, "function") || !d) e = e || d || null, d = null; b = Object(b), c = +c || 0; var f = {}, h, i; for (i in b) b[g](i) && Q(i) != i && Q(i) + "%" != i && (h = !0, f[i] = b[i]); if (!h) return new cD(b, c); d && (f.easing = d), e && (f.callback = e); return new cD({ 100: f }, c) }, cl.animate = function (b, c, d, e) { var f = this; if (f.removed) { e && e.call(f); return f } var g = b instanceof cD ? b : a.animation(b, c, d, e); cE(g, f, g.percents[0], null, f.attr()); return f }, cl.setTime = function (a, b) { a && b != null && this.status(a, y(b, a.ms) / a.ms); return this }, cl.status = function (a, b) { var c = [], d = 0, e, f; if (b != null) { cE(a, this, -1, y(b, 1)); return this } e = cy.length; for (; d < e; d++) { f = cy[d]; if (f.el.id == this.id && (!a || f.anim == a)) { if (a) return f.status; c.push({ anim: f.anim, status: f.status }) } } if (a) return 0; return c }, cl.pause = function (a) { for (var b = 0; b < cy.length; b++)cy[b].el.id == this.id && (!a || cy[b].anim == a) && eve("raphael.anim.pause." + this.id, this, cy[b].anim) !== !1 && (cy[b].paused = !0); return this }, cl.resume = function (a) { for (var b = 0; b < cy.length; b++)if (cy[b].el.id == this.id && (!a || cy[b].anim == a)) { var c = cy[b]; eve("raphael.anim.resume." + this.id, this, c.anim) !== !1 && (delete c.paused, this.status(c.anim, c.status)) } return this }, cl.stop = function (a) { for (var b = 0; b < cy.length; b++)cy[b].el.id == this.id && (!a || cy[b].anim == a) && eve("raphael.anim.stop." + this.id, this, cy[b].anim) !== !1 && cy.splice(b--, 1); return this }, eve.on("raphael.remove", cF), eve.on("raphael.clear", cF), cl.toString = function () { return "RaphaÃ«lâ€™s object" }; var cG = function (a) { this.items = [], this.length = 0, this.type = "set"; if (a) for (var b = 0, c = a.length; b < c; b++)a[b] && (a[b].constructor == cl.constructor || a[b].constructor == cG) && (this[this.items.length] = this.items[this.items.length] = a[b], this.length++) }, cH = cG.prototype; cH.push = function () { var a, b; for (var c = 0, d = arguments.length; c < d; c++)a = arguments[c], a && (a.constructor == cl.constructor || a.constructor == cG) && (b = this.items.length, this[b] = this.items[b] = a, this.length++); return this }, cH.pop = function () { this.length && delete this[this.length--]; return this.items.pop() }, cH.forEach = function (a, b) { for (var c = 0, d = this.items.length; c < d; c++)if (a.call(b, this.items[c], c) === !1) return this; return this }; for (var cI in cl) cl[g](cI) && (cH[cI] = function (a) { return function () { var b = arguments; return this.forEach(function (c) { c[a][m](c, b) }) } }(cI)); cH.attr = function (b, c) { if (b && a.is(b, E) && a.is(b[0], "object")) for (var d = 0, e = b.length; d < e; d++)this.items[d].attr(b[d]); else for (var f = 0, g = this.items.length; f < g; f++)this.items[f].attr(b, c); return this }, cH.clear = function () { while (this.length) this.pop() }, cH.splice = function (a, b, c) { a = a < 0 ? x(this.length + a, 0) : a, b = x(0, y(this.length - a, b)); var d = [], e = [], f = [], g; for (g = 2; g < arguments.length; g++)f.push(arguments[g]); for (g = 0; g < b; g++)e.push(this[a + g]); for (; g < this.length - a; g++)d.push(this[a + g]); var h = f.length; for (g = 0; g < h + d.length; g++)this.items[a + g] = this[a + g] = g < h ? f[g] : d[g - h]; g = this.items.length = this.length -= b - h; while (this[g]) delete this[g++]; return new cG(e) }, cH.exclude = function (a) { for (var b = 0, c = this.length; b < c; b++)if (this[b] == a) { this.splice(b, 1); return !0 } }, cH.animate = function (b, c, d, e) { (a.is(d, "function") || !d) && (e = d || null); var f = this.items.length, g = f, h, i = this, j; if (!f) return this; e && (j = function () { !--f && e.call(i) }), d = a.is(d, D) ? d : j; var k = a.animation(b, c, d, j); h = this.items[--g].animate(k); while (g--) this.items[g] && !this.items[g].removed && this.items[g].animateWith(h, k, k); return this }, cH.insertAfter = function (a) { var b = this.items.length; while (b--) this.items[b].insertAfter(a); return this }, cH.getBBox = function () { var a = [], b = [], c = [], d = []; for (var e = this.items.length; e--;)if (!this.items[e].removed) { var f = this.items[e].getBBox(); a.push(f.x), b.push(f.y), c.push(f.x + f.width), d.push(f.y + f.height) } a = y[m](0, a), b = y[m](0, b), c = x[m](0, c), d = x[m](0, d); return { x: a, y: b, x2: c, y2: d, width: c - a, height: d - b } }, cH.clone = function (a) { a = new cG; for (var b = 0, c = this.items.length; b < c; b++)a.push(this.items[b].clone()); return a }, cH.toString = function () { return "RaphaÃ«lâ€˜s set" }, a.registerFont = function (a) { if (!a.face) return a; this.fonts = this.fonts || {}; var b = { w: a.w, face: {}, glyphs: {} }, c = a.face["font-family"]; for (var d in a.face) a.face[g](d) && (b.face[d] = a.face[d]); this.fonts[c] ? this.fonts[c].push(b) : this.fonts[c] = [b]; if (!a.svg) { b.face["units-per-em"] = R(a.face["units-per-em"], 10); for (var e in a.glyphs) if (a.glyphs[g](e)) { var f = a.glyphs[e]; b.glyphs[e] = { w: f.w, k: {}, d: f.d && "M" + f.d.replace(/[mlcxtrv]/g, function (a) { return { l: "L", c: "C", x: "z", t: "m", r: "l", v: "c" }[a] || "M" }) + "z" }; if (f.k) for (var h in f.k) f[g](h) && (b.glyphs[e].k[h] = f.k[h]) } } return a }, k.getFont = function (b, c, d, e) { e = e || "normal", d = d || "normal", c = +c || { normal: 400, bold: 700, lighter: 300, bolder: 800 }[c] || 400; if (!!a.fonts) { var f = a.fonts[b]; if (!f) { var h = new RegExp("(^|\\s)" + b.replace(/[^\w\d\s+!~.:_-]/g, p) + "(\\s|$)", "i"); for (var i in a.fonts) if (a.fonts[g](i) && h.test(i)) { f = a.fonts[i]; break } } var j; if (f) for (var k = 0, l = f.length; k < l; k++) { j = f[k]; if (j.face["font-weight"] == c && (j.face["font-style"] == d || !j.face["font-style"]) && j.face["font-stretch"] == e) break } return j } }, k.print = function (b, d, e, f, g, h, i) { h = h || "middle", i = x(y(i || 0, 1), -1); var j = r(e)[s](p), k = 0, l = 0, m = p, n; a.is(f, e) && (f = this.getFont(f)); if (f) { n = (g || 16) / f.face["units-per-em"]; var o = f.face.bbox[s](c), q = +o[0], t = o[3] - o[1], u = 0, v = +o[1] + (h == "baseline" ? t + +f.face.descent : t / 2); for (var w = 0, z = j.length; w < z; w++) { if (j[w] == "\n") k = 0, B = 0, l = 0, u += t; else { var A = l && f.glyphs[j[w - 1]] || {}, B = f.glyphs[j[w]]; k += l ? (A.w || f.w) + (A.k && A.k[j[w]] || 0) + f.w * i : 0, l = 1 } B && B.d && (m += a.transformPath(B.d, ["t", k * n, u * n, "s", n, n, q, v, "t", (b - q) / n, (d - v) / n])) } } return this.path(m).attr({ fill: "#000", stroke: "none" }) }, k.add = function (b) { if (a.is(b, "array")) { var c = this.set(), e = 0, f = b.length, h; for (; e < f; e++)h = b[e] || {}, d[g](h.type) && c.push(this[h.type]().attr(h)) } return c }, a.format = function (b, c) { var d = a.is(c, E) ? [0][n](c) : arguments; b && a.is(b, D) && d.length - 1 && (b = b.replace(e, function (a, b) { return d[++b] == null ? p : d[b] })); return b || p }, a.fullfill = function () { var a = /\{([^\}]+)\}/g, b = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, c = function (a, c, d) { var e = d; c.replace(b, function (a, b, c, d, f) { b = b || d, e && (b in e && (e = e[b]), typeof e == "function" && f && (e = e())) }), e = (e == null || e == d ? a : e) + ""; return e }; return function (b, d) { return String(b).replace(a, function (a, b) { return c(a, b, d) }) } }(), a.ninja = function () { i.was ? h.win.Raphael = i.is : delete Raphael; return a }, a.st = cH, function (b, c, d) { function e() { /in/.test(b.readyState) ? setTimeout(e, 9) : a.eve("raphael.DOMload") } b.readyState == null && b.addEventListener && (b.addEventListener(c, d = function () { b.removeEventListener(c, d, !1), b.readyState = "complete" }, !1), b.readyState = "loading"), e() }(document, "DOMContentLoaded"), i.was ? h.win.Raphael = a : Raphael = a, eve.on("raphael.DOMload", function () { b = !0 }) }(), window.Raphael.svg && function (a) { var b = "hasOwnProperty", c = String, d = parseFloat, e = parseInt, f = Math, g = f.max, h = f.abs, i = f.pow, j = /[, ]+/, k = a.eve, l = "", m = " ", n = "http://www.w3.org/1999/xlink", o = { block: "M5,0 0,2.5 5,5z", classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z", diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z", open: "M6,1 1,3.5 6,6", oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z" }, p = {}; a.toString = function () { return "Your browser supports SVG.\nYou are running RaphaÃ«l " + this.version }; var q = function (d, e) { if (e) { typeof d == "string" && (d = q(d)); for (var f in e) e[b](f) && (f.substring(0, 6) == "xlink:" ? d.setAttributeNS(n, f.substring(6), c(e[f])) : d.setAttribute(f, c(e[f]))) } else d = a._g.doc.createElementNS("http://www.w3.org/2000/svg", d), d.style && (d.style.webkitTapHighlightColor = "rgba(0,0,0,0)"); return d }, r = function (b, e) { var j = "linear", k = b.id + e, m = .5, n = .5, o = b.node, p = b.paper, r = o.style, s = a._g.doc.getElementById(k); if (!s) { e = c(e).replace(a._radial_gradient, function (a, b, c) { j = "radial"; if (b && c) { m = d(b), n = d(c); var e = (n > .5) * 2 - 1; i(m - .5, 2) + i(n - .5, 2) > .25 && (n = f.sqrt(.25 - i(m - .5, 2)) * e + .5) && n != .5 && (n = n.toFixed(5) - 1e-5 * e) } return l }), e = e.split(/\s*\-\s*/); if (j == "linear") { var t = e.shift(); t = -d(t); if (isNaN(t)) return null; var u = [0, 0, f.cos(a.rad(t)), f.sin(a.rad(t))], v = 1 / (g(h(u[2]), h(u[3])) || 1); u[2] *= v, u[3] *= v, u[2] < 0 && (u[0] = -u[2], u[2] = 0), u[3] < 0 && (u[1] = -u[3], u[3] = 0) } var w = a._parseDots(e); if (!w) return null; k = k.replace(/[\(\)\s,\xb0#]/g, "_"), b.gradient && k != b.gradient.id && (p.defs.removeChild(b.gradient), delete b.gradient); if (!b.gradient) { s = q(j + "Gradient", { id: k }), b.gradient = s, q(s, j == "radial" ? { fx: m, fy: n } : { x1: u[0], y1: u[1], x2: u[2], y2: u[3], gradientTransform: b.matrix.invert() }), p.defs.appendChild(s); for (var x = 0, y = w.length; x < y; x++)s.appendChild(q("stop", { offset: w[x].offset ? w[x].offset : x ? "100%" : "0%", "stop-color": w[x].color || "#fff" })) } } q(o, { fill: "url(#" + k + ")", opacity: 1, "fill-opacity": 1 }), r.fill = l, r.opacity = 1, r.fillOpacity = 1; return 1 }, s = function (a) { var b = a.getBBox(1); q(a.pattern, { patternTransform: a.matrix.invert() + " translate(" + b.x + "," + b.y + ")" }) }, t = function (d, e, f) { if (d.type == "path") { var g = c(e).toLowerCase().split("-"), h = d.paper, i = f ? "end" : "start", j = d.node, k = d.attrs, m = k["stroke-width"], n = g.length, r = "classic", s, t, u, v, w, x = 3, y = 3, z = 5; while (n--) switch (g[n]) { case "block": case "classic": case "oval": case "diamond": case "open": case "none": r = g[n]; break; case "wide": y = 5; break; case "narrow": y = 2; break; case "long": x = 5; break; case "short": x = 2 }r == "open" ? (x += 2, y += 2, z += 2, u = 1, v = f ? 4 : 1, w = { fill: "none", stroke: k.stroke }) : (v = u = x / 2, w = { fill: k.stroke, stroke: "none" }), d._.arrows ? f ? (d._.arrows.endPath && p[d._.arrows.endPath]--, d._.arrows.endMarker && p[d._.arrows.endMarker]--) : (d._.arrows.startPath && p[d._.arrows.startPath]--, d._.arrows.startMarker && p[d._.arrows.startMarker]--) : d._.arrows = {}; if (r != "none") { var A = "raphael-marker-" + r, B = "raphael-marker-" + i + r + x + y; a._g.doc.getElementById(A) ? p[A]++ : (h.defs.appendChild(q(q("path"), { "stroke-linecap": "round", d: o[r], id: A })), p[A] = 1); var C = a._g.doc.getElementById(B), D; C ? (p[B]++, D = C.getElementsByTagName("use")[0]) : (C = q(q("marker"), { id: B, markerHeight: y, markerWidth: x, orient: "auto", refX: v, refY: y / 2 }), D = q(q("use"), { "xlink:href": "#" + A, transform: (f ? "rotate(180 " + x / 2 + " " + y / 2 + ") " : l) + "scale(" + x / z + "," + y / z + ")", "stroke-width": (1 / ((x / z + y / z) / 2)).toFixed(4) }), C.appendChild(D), h.defs.appendChild(C), p[B] = 1), q(D, w); var F = u * (r != "diamond" && r != "oval"); f ? (s = d._.arrows.startdx * m || 0, t = a.getTotalLength(k.path) - F * m) : (s = F * m, t = a.getTotalLength(k.path) - (d._.arrows.enddx * m || 0)), w = {}, w["marker-" + i] = "url(#" + B + ")"; if (t || s) w.d = Raphael.getSubpath(k.path, s, t); q(j, w), d._.arrows[i + "Path"] = A, d._.arrows[i + "Marker"] = B, d._.arrows[i + "dx"] = F, d._.arrows[i + "Type"] = r, d._.arrows[i + "String"] = e } else f ? (s = d._.arrows.startdx * m || 0, t = a.getTotalLength(k.path) - s) : (s = 0, t = a.getTotalLength(k.path) - (d._.arrows.enddx * m || 0)), d._.arrows[i + "Path"] && q(j, { d: Raphael.getSubpath(k.path, s, t) }), delete d._.arrows[i + "Path"], delete d._.arrows[i + "Marker"], delete d._.arrows[i + "dx"], delete d._.arrows[i + "Type"], delete d._.arrows[i + "String"]; for (w in p) if (p[b](w) && !p[w]) { var G = a._g.doc.getElementById(w); G && G.parentNode.removeChild(G) } } }, u = { "": [0], none: [0], "-": [3, 1], ".": [1, 1], "-.": [3, 1, 1, 1], "-..": [3, 1, 1, 1, 1, 1], ". ": [1, 3], "- ": [4, 3], "--": [8, 3], "- .": [4, 3, 1, 3], "--.": [8, 3, 1, 3], "--..": [8, 3, 1, 3, 1, 3] }, v = function (a, b, d) { b = u[c(b).toLowerCase()]; if (b) { var e = a.attrs["stroke-width"] || "1", f = { round: e, square: e, butt: 0 }[a.attrs["stroke-linecap"] || d["stroke-linecap"]] || 0, g = [], h = b.length; while (h--) g[h] = b[h] * e + (h % 2 ? 1 : -1) * f; q(a.node, { "stroke-dasharray": g.join(",") }) } }, w = function (d, f) { var i = d.node, k = d.attrs, m = i.style.visibility; i.style.visibility = "hidden"; for (var o in f) if (f[b](o)) { if (!a._availableAttrs[b](o)) continue; var p = f[o]; k[o] = p; switch (o) { case "blur": d.blur(p); break; case "href": case "title": case "target": var u = i.parentNode; if (u.tagName.toLowerCase() != "a") { var w = q("a"); u.insertBefore(w, i), w.appendChild(i), u = w } o == "target" ? u.setAttributeNS(n, "show", p == "blank" ? "new" : p) : u.setAttributeNS(n, o, p); break; case "cursor": i.style.cursor = p; break; case "transform": d.transform(p); break; case "arrow-start": t(d, p); break; case "arrow-end": t(d, p, 1); break; case "clip-rect": var x = c(p).split(j); if (x.length == 4) { d.clip && d.clip.parentNode.parentNode.removeChild(d.clip.parentNode); var z = q("clipPath"), A = q("rect"); z.id = a.createUUID(), q(A, { x: x[0], y: x[1], width: x[2], height: x[3] }), z.appendChild(A), d.paper.defs.appendChild(z), q(i, { "clip-path": "url(#" + z.id + ")" }), d.clip = A } if (!p) { var B = i.getAttribute("clip-path"); if (B) { var C = a._g.doc.getElementById(B.replace(/(^url\(#|\)$)/g, l)); C && C.parentNode.removeChild(C), q(i, { "clip-path": l }), delete d.clip } } break; case "path": d.type == "path" && (q(i, { d: p ? k.path = a._pathToAbsolute(p) : "M0,0" }), d._.dirty = 1, d._.arrows && ("startString" in d._.arrows && t(d, d._.arrows.startString), "endString" in d._.arrows && t(d, d._.arrows.endString, 1))); break; case "width": i.setAttribute(o, p), d._.dirty = 1; if (k.fx) o = "x", p = k.x; else break; case "x": k.fx && (p = -k.x - (k.width || 0)); case "rx": if (o == "rx" && d.type == "rect") break; case "cx": i.setAttribute(o, p), d.pattern && s(d), d._.dirty = 1; break; case "height": i.setAttribute(o, p), d._.dirty = 1; if (k.fy) o = "y", p = k.y; else break; case "y": k.fy && (p = -k.y - (k.height || 0)); case "ry": if (o == "ry" && d.type == "rect") break; case "cy": i.setAttribute(o, p), d.pattern && s(d), d._.dirty = 1; break; case "r": d.type == "rect" ? q(i, { rx: p, ry: p }) : i.setAttribute(o, p), d._.dirty = 1; break; case "src": d.type == "image" && i.setAttributeNS(n, "href", p); break; case "stroke-width": if (d._.sx != 1 || d._.sy != 1) p /= g(h(d._.sx), h(d._.sy)) || 1; d.paper._vbSize && (p *= d.paper._vbSize), i.setAttribute(o, p), k["stroke-dasharray"] && v(d, k["stroke-dasharray"], f), d._.arrows && ("startString" in d._.arrows && t(d, d._.arrows.startString), "endString" in d._.arrows && t(d, d._.arrows.endString, 1)); break; case "stroke-dasharray": v(d, p, f); break; case "fill": var D = c(p).match(a._ISURL); if (D) { z = q("pattern"); var F = q("image"); z.id = a.createUUID(), q(z, { x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1 }), q(F, { x: 0, y: 0, "xlink:href": D[1] }), z.appendChild(F), function (b) { a._preload(D[1], function () { var a = this.offsetWidth, c = this.offsetHeight; q(b, { width: a, height: c }), q(F, { width: a, height: c }), d.paper.safari() }) }(z), d.paper.defs.appendChild(z), q(i, { fill: "url(#" + z.id + ")" }), d.pattern = z, d.pattern && s(d); break } var G = a.getRGB(p); if (!G.error) delete f.gradient, delete k.gradient, !a.is(k.opacity, "undefined") && a.is(f.opacity, "undefined") && q(i, { opacity: k.opacity }), !a.is(k["fill-opacity"], "undefined") && a.is(f["fill-opacity"], "undefined") && q(i, { "fill-opacity": k["fill-opacity"] }); else if ((d.type == "circle" || d.type == "ellipse" || c(p).charAt() != "r") && r(d, p)) { if ("opacity" in k || "fill-opacity" in k) { var H = a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g, l)); if (H) { var I = H.getElementsByTagName("stop"); q(I[I.length - 1], { "stop-opacity": ("opacity" in k ? k.opacity : 1) * ("fill-opacity" in k ? k["fill-opacity"] : 1) }) } } k.gradient = p, k.fill = "none"; break } G[b]("opacity") && q(i, { "fill-opacity": G.opacity > 1 ? G.opacity / 100 : G.opacity }); case "stroke": G = a.getRGB(p), i.setAttribute(o, G.hex), o == "stroke" && G[b]("opacity") && q(i, { "stroke-opacity": G.opacity > 1 ? G.opacity / 100 : G.opacity }), o == "stroke" && d._.arrows && ("startString" in d._.arrows && t(d, d._.arrows.startString), "endString" in d._.arrows && t(d, d._.arrows.endString, 1)); break; case "gradient": (d.type == "circle" || d.type == "ellipse" || c(p).charAt() != "r") && r(d, p); break; case "opacity": k.gradient && !k[b]("stroke-opacity") && q(i, { "stroke-opacity": p > 1 ? p / 100 : p }); case "fill-opacity": if (k.gradient) { H = a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g, l)), H && (I = H.getElementsByTagName("stop"), q(I[I.length - 1], { "stop-opacity": p })); break }; default: o == "font-size" && (p = e(p, 10) + "px"); var J = o.replace(/(\-.)/g, function (a) { return a.substring(1).toUpperCase() }); i.style[J] = p, d._.dirty = 1, i.setAttribute(o, p) } } y(d, f), i.style.visibility = m }, x = 1.2, y = function (d, f) { if (d.type == "text" && !!(f[b]("text") || f[b]("font") || f[b]("font-size") || f[b]("x") || f[b]("y"))) { var g = d.attrs, h = d.node, i = h.firstChild ? e(a._g.doc.defaultView.getComputedStyle(h.firstChild, l).getPropertyValue("font-size"), 10) : 10; if (f[b]("text")) { g.text = f.text; while (h.firstChild) h.removeChild(h.firstChild); var j = c(f.text).split("\n"), k = [], m; for (var n = 0, o = j.length; n < o; n++)m = q("tspan"), n && q(m, { dy: i * x, x: g.x }), m.appendChild(a._g.doc.createTextNode(j[n])), h.appendChild(m), k[n] = m } else { k = h.getElementsByTagName("tspan"); for (n = 0, o = k.length; n < o; n++)n ? q(k[n], { dy: i * x, x: g.x }) : q(k[0], { dy: 0 }) } q(h, { x: g.x, y: g.y }), d._.dirty = 1; var p = d._getBBox(), r = g.y - (p.y + p.height / 2); r && a.is(r, "finite") && q(k[0], { dy: r }) } }, z = function (b, c) { var d = 0, e = 0; this[0] = this.node = b, b.raphael = !0, this.id = a._oid++, b.raphaelid = this.id, this.matrix = a.matrix(), this.realPath = null, this.paper = c, this.attrs = this.attrs || {}, this._ = { transform: [], sx: 1, sy: 1, deg: 0, dx: 0, dy: 0, dirty: 1 }, !c.bottom && (c.bottom = this), this.prev = c.top, c.top && (c.top.next = this), c.top = this, this.next = null }, A = a.el; z.prototype = A, A.constructor = z, a._engine.path = function (a, b) { var c = q("path"); b.canvas && b.canvas.appendChild(c); var d = new z(c, b); d.type = "path", w(d, { fill: "none", stroke: "#000", path: a }); return d }, A.rotate = function (a, b, e) { if (this.removed) return this; a = c(a).split(j), a.length - 1 && (b = d(a[1]), e = d(a[2])), a = d(a[0]), e == null && (b = e); if (b == null || e == null) { var f = this.getBBox(1); b = f.x + f.width / 2, e = f.y + f.height / 2 } this.transform(this._.transform.concat([["r", a, b, e]])); return this }, A.scale = function (a, b, e, f) { if (this.removed) return this; a = c(a).split(j), a.length - 1 && (b = d(a[1]), e = d(a[2]), f = d(a[3])), a = d(a[0]), b == null && (b = a), f == null && (e = f); if (e == null || f == null) var g = this.getBBox(1); e = e == null ? g.x + g.width / 2 : e, f = f == null ? g.y + g.height / 2 : f, this.transform(this._.transform.concat([["s", a, b, e, f]])); return this }, A.translate = function (a, b) { if (this.removed) return this; a = c(a).split(j), a.length - 1 && (b = d(a[1])), a = d(a[0]) || 0, b = +b || 0, this.transform(this._.transform.concat([["t", a, b]])); return this }, A.transform = function (c) { var d = this._; if (c == null) return d.transform; a._extractTransform(this, c), this.clip && q(this.clip, { transform: this.matrix.invert() }), this.pattern && s(this), this.node && q(this.node, { transform: this.matrix }); if (d.sx != 1 || d.sy != 1) { var e = this.attrs[b]("stroke-width") ? this.attrs["stroke-width"] : 1; this.attr({ "stroke-width": e }) } return this }, A.hide = function () { !this.removed && this.paper.safari(this.node.style.display = "none"); return this }, A.show = function () { !this.removed && this.paper.safari(this.node.style.display = ""); return this }, A.remove = function () { if (!this.removed && !!this.node.parentNode) { var b = this.paper; b.__set__ && b.__set__.exclude(this), k.unbind("raphael.*.*." + this.id), this.gradient && b.defs.removeChild(this.gradient), a._tear(this, b), this.node.parentNode.tagName.toLowerCase() == "a" ? this.node.parentNode.parentNode.removeChild(this.node.parentNode) : this.node.parentNode.removeChild(this.node); for (var c in this) this[c] = typeof this[c] == "function" ? a._removedFactory(c) : null; this.removed = !0 } }, A._getBBox = function () { if (this.node.style.display == "none") { this.show(); var a = !0 } var b = {}; try { b = this.node.getBBox() } catch (c) { } finally { b = b || {} } a && this.hide(); return b }, A.attr = function (c, d) { if (this.removed) return this; if (c == null) { var e = {}; for (var f in this.attrs) this.attrs[b](f) && (e[f] = this.attrs[f]); e.gradient && e.fill == "none" && (e.fill = e.gradient) && delete e.gradient, e.transform = this._.transform; return e } if (d == null && a.is(c, "string")) { if (c == "fill" && this.attrs.fill == "none" && this.attrs.gradient) return this.attrs.gradient; if (c == "transform") return this._.transform; var g = c.split(j), h = {}; for (var i = 0, l = g.length; i < l; i++)c = g[i], c in this.attrs ? h[c] = this.attrs[c] : a.is(this.paper.customAttributes[c], "function") ? h[c] = this.paper.customAttributes[c].def : h[c] = a._availableAttrs[c]; return l - 1 ? h : h[g[0]] } if (d == null && a.is(c, "array")) { h = {}; for (i = 0, l = c.length; i < l; i++)h[c[i]] = this.attr(c[i]); return h } if (d != null) { var m = {}; m[c] = d } else c != null && a.is(c, "object") && (m = c); for (var n in m) k("raphael.attr." + n + "." + this.id, this, m[n]); for (n in this.paper.customAttributes) if (this.paper.customAttributes[b](n) && m[b](n) && a.is(this.paper.customAttributes[n], "function")) { var o = this.paper.customAttributes[n].apply(this, [].concat(m[n])); this.attrs[n] = m[n]; for (var p in o) o[b](p) && (m[p] = o[p]) } w(this, m); return this }, A.toFront = function () { if (this.removed) return this; this.node.parentNode.tagName.toLowerCase() == "a" ? this.node.parentNode.parentNode.appendChild(this.node.parentNode) : this.node.parentNode.appendChild(this.node); var b = this.paper; b.top != this && a._tofront(this, b); return this }, A.toBack = function () { if (this.removed) return this; var b = this.node.parentNode; b.tagName.toLowerCase() == "a" ? b.parentNode.insertBefore(this.node.parentNode, this.node.parentNode.parentNode.firstChild) : b.firstChild != this.node && b.insertBefore(this.node, this.node.parentNode.firstChild), a._toback(this, this.paper); var c = this.paper; return this }, A.insertAfter = function (b) { if (this.removed) return this; var c = b.node || b[b.length - 1].node; c.nextSibling ? c.parentNode.insertBefore(this.node, c.nextSibling) : c.parentNode.appendChild(this.node), a._insertafter(this, b, this.paper); return this }, A.insertBefore = function (b) { if (this.removed) return this; var c = b.node || b[0].node; c.parentNode.insertBefore(this.node, c), a._insertbefore(this, b, this.paper); return this }, A.blur = function (b) { var c = this; if (+b !== 0) { var d = q("filter"), e = q("feGaussianBlur"); c.attrs.blur = b, d.id = a.createUUID(), q(e, { stdDeviation: +b || 1.5 }), d.appendChild(e), c.paper.defs.appendChild(d), c._blur = d, q(c.node, { filter: "url(#" + d.id + ")" }) } else c._blur && (c._blur.parentNode.removeChild(c._blur), delete c._blur, delete c.attrs.blur), c.node.removeAttribute("filter") }, a._engine.circle = function (a, b, c, d) { var e = q("circle"); a.canvas && a.canvas.appendChild(e); var f = new z(e, a); f.attrs = { cx: b, cy: c, r: d, fill: "none", stroke: "#000" }, f.type = "circle", q(e, f.attrs); return f }, a._engine.rect = function (a, b, c, d, e, f) { var g = q("rect"); a.canvas && a.canvas.appendChild(g); var h = new z(g, a); h.attrs = { x: b, y: c, width: d, height: e, r: f || 0, rx: f || 0, ry: f || 0, fill: "none", stroke: "#000" }, h.type = "rect", q(g, h.attrs); return h }, a._engine.ellipse = function (a, b, c, d, e) { var f = q("ellipse"); a.canvas && a.canvas.appendChild(f); var g = new z(f, a); g.attrs = { cx: b, cy: c, rx: d, ry: e, fill: "none", stroke: "#000" }, g.type = "ellipse", q(f, g.attrs); return g }, a._engine.image = function (a, b, c, d, e, f) { var g = q("image"); q(g, { x: c, y: d, width: e, height: f, preserveAspectRatio: "none" }), g.setAttributeNS(n, "href", b), a.canvas && a.canvas.appendChild(g); var h = new z(g, a); h.attrs = { x: c, y: d, width: e, height: f, src: b }, h.type = "image"; return h }, a._engine.text = function (b, c, d, e) { var f = q("text"); b.canvas && b.canvas.appendChild(f); var g = new z(f, b); g.attrs = { x: c, y: d, "text-anchor": "middle", text: e, font: a._availableAttrs.font, stroke: "none", fill: "#000" }, g.type = "text", w(g, g.attrs); return g }, a._engine.setSize = function (a, b) { this.width = a || this.width, this.height = b || this.height, this.canvas.setAttribute("width", this.width), this.canvas.setAttribute("height", this.height), this._viewBox && this.setViewBox.apply(this, this._viewBox); return this }, a._engine.create = function () { var b = a._getContainer.apply(0, arguments), c = b && b.container, d = b.x, e = b.y, f = b.width, g = b.height; if (!c) throw new Error("SVG container not found."); var h = q("svg"), i = "overflow:hidden;", j; d = d || 0, e = e || 0, f = f || 512, g = g || 342, q(h, { height: g, version: 1.1, width: f, xmlns: "http://www.w3.org/2000/svg" }), c == 1 ? (h.style.cssText = i + "position:absolute;left:" + d + "px;top:" + e + "px", a._g.doc.body.appendChild(h), j = 1) : (h.style.cssText = i + "", c.firstChild ? c.insertBefore(h, c.firstChild) : c.appendChild(h)), c = new a._Paper, c.width = f, c.height = g, c.canvas = h, c.clear(), c._left = c._top = 0, j && (c.renderfix = function () { }), c.renderfix(); return c }, a._engine.setViewBox = function (a, b, c, d, e) { k("raphael.setViewBox", this, this._viewBox, [a, b, c, d, e]); var f = g(c / this.width, d / this.height), h = this.top, i = e ? "meet" : "xMinYMin", j, l; a == null ? (this._vbSize && (f = 1), delete this._vbSize, j = "0 0 " + this.width + m + this.height) : (this._vbSize = f, j = a + m + b + m + c + m + d), q(this.canvas, { viewBox: j, preserveAspectRatio: i }); while (f && h) l = "stroke-width" in h.attrs ? h.attrs["stroke-width"] : 1, h.attr({ "stroke-width": l }), h._.dirty = 1, h._.dirtyT = 1, h = h.prev; this._viewBox = [a, b, c, d, !!e]; return this }, a.prototype.renderfix = function () { var a = this.canvas, b = a.style, c; try { c = a.getScreenCTM() || a.createSVGMatrix() } catch (d) { c = a.createSVGMatrix() } var e = -c.e % 1, f = -c.f % 1; if (e || f) e && (this._left = (this._left + e) % 1, b.left = this._left + "px"), f && (this._top = (this._top + f) % 1, b.top = this._top + "px") }, a.prototype.clear = function () { a.eve("raphael.clear", this); var b = this.canvas; while (b.firstChild) b.removeChild(b.firstChild); this.bottom = this.top = null, (this.desc = q("desc")).appendChild(a._g.doc.createTextNode("Created with RaphaÃ«l " + a.version)), b.appendChild(this.desc), b.appendChild(this.defs = q("defs")) }, a.prototype.remove = function () { k("raphael.remove", this), this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas); for (var b in this) this[b] = typeof this[b] == "function" ? a._removedFactory(b) : null }; var B = a.st; for (var C in A) A[b](C) && !B[b](C) && (B[C] = function (a) { return function () { var b = arguments; return this.forEach(function (c) { c[a].apply(c, b) }) } }(C)) }(window.Raphael), window.Raphael.vml && function (a) { var b = "hasOwnProperty", c = String, d = parseFloat, e = Math, f = e.round, g = e.max, h = e.min, i = e.abs, j = "fill", k = /[, ]+/, l = a.eve, m = " progid:DXImageTransform.Microsoft", n = " ", o = "", p = { M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x" }, q = /([clmz]),?([^clmz]*)/gi, r = / progid:\S+Blur\([^\)]+\)/g, s = /-?[^,\s-]+/g, t = "position:absolute;left:0;top:0;width:1px;height:1px", u = 21600, v = { path: 1, rect: 1, image: 1 }, w = { circle: 1, ellipse: 1 }, x = function (b) { var d = /[ahqstv]/ig, e = a._pathToAbsolute; c(b).match(d) && (e = a._path2curve), d = /[clmz]/g; if (e == a._pathToAbsolute && !c(b).match(d)) { var g = c(b).replace(q, function (a, b, c) { var d = [], e = b.toLowerCase() == "m", g = p[b]; c.replace(s, function (a) { e && d.length == 2 && (g += d + p[b == "m" ? "l" : "L"], d = []), d.push(f(a * u)) }); return g + d }); return g } var h = e(b), i, j; g = []; for (var k = 0, l = h.length; k < l; k++) { i = h[k], j = h[k][0].toLowerCase(), j == "z" && (j = "x"); for (var m = 1, r = i.length; m < r; m++)j += f(i[m] * u) + (m != r - 1 ? "," : o); g.push(j) } return g.join(n) }, y = function (b, c, d) { var e = a.matrix(); e.rotate(-b, .5, .5); return { dx: e.x(c, d), dy: e.y(c, d) } }, z = function (a, b, c, d, e, f) { var g = a._, h = a.matrix, k = g.fillpos, l = a.node, m = l.style, o = 1, p = "", q, r = u / b, s = u / c; m.visibility = "hidden"; if (!!b && !!c) { l.coordsize = i(r) + n + i(s), m.rotation = f * (b * c < 0 ? -1 : 1); if (f) { var t = y(f, d, e); d = t.dx, e = t.dy } b < 0 && (p += "x"), c < 0 && (p += " y") && (o = -1), m.flip = p, l.coordorigin = d * -r + n + e * -s; if (k || g.fillsize) { var v = l.getElementsByTagName(j); v = v && v[0], l.removeChild(v), k && (t = y(f, h.x(k[0], k[1]), h.y(k[0], k[1])), v.position = t.dx * o + n + t.dy * o), g.fillsize && (v.size = g.fillsize[0] * i(b) + n + g.fillsize[1] * i(c)), l.appendChild(v) } m.visibility = "visible" } }; a.toString = function () { return "Your browser doesnâ€™t support SVG. Falling down to VML.\nYou are running RaphaÃ«l " + this.version }; var A = function (a, b, d) { var e = c(b).toLowerCase().split("-"), f = d ? "end" : "start", g = e.length, h = "classic", i = "medium", j = "medium"; while (g--) switch (e[g]) { case "block": case "classic": case "oval": case "diamond": case "open": case "none": h = e[g]; break; case "wide": case "narrow": j = e[g]; break; case "long": case "short": i = e[g] }var k = a.node.getElementsByTagName("stroke")[0]; k[f + "arrow"] = h, k[f + "arrowlength"] = i, k[f + "arrowwidth"] = j }, B = function (e, i) { e.attrs = e.attrs || {}; var l = e.node, m = e.attrs, p = l.style, q, r = v[e.type] && (i.x != m.x || i.y != m.y || i.width != m.width || i.height != m.height || i.cx != m.cx || i.cy != m.cy || i.rx != m.rx || i.ry != m.ry || i.r != m.r), s = w[e.type] && (m.cx != i.cx || m.cy != i.cy || m.r != i.r || m.rx != i.rx || m.ry != i.ry), t = e; for (var y in i) i[b](y) && (m[y] = i[y]); r && (m.path = a._getPath[e.type](e), e._.dirty = 1), i.href && (l.href = i.href), i.title && (l.title = i.title), i.target && (l.target = i.target), i.cursor && (p.cursor = i.cursor), "blur" in i && e.blur(i.blur); if (i.path && e.type == "path" || r) l.path = x(~c(m.path).toLowerCase().indexOf("r") ? a._pathToAbsolute(m.path) : m.path), e.type == "image" && (e._.fillpos = [m.x, m.y], e._.fillsize = [m.width, m.height], z(e, 1, 1, 0, 0, 0)); "transform" in i && e.transform(i.transform); if (s) { var B = +m.cx, D = +m.cy, E = +m.rx || +m.r || 0, G = +m.ry || +m.r || 0; l.path = a.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", f((B - E) * u), f((D - G) * u), f((B + E) * u), f((D + G) * u), f(B * u)) } if ("clip-rect" in i) { var H = c(i["clip-rect"]).split(k); if (H.length == 4) { H[2] = +H[2] + +H[0], H[3] = +H[3] + +H[1]; var I = l.clipRect || a._g.doc.createElement("div"), J = I.style; J.clip = a.format("rect({1}px {2}px {3}px {0}px)", H), l.clipRect || (J.position = "absolute", J.top = 0, J.left = 0, J.width = e.paper.width + "px", J.height = e.paper.height + "px", l.parentNode.insertBefore(I, l), I.appendChild(l), l.clipRect = I) } i["clip-rect"] || l.clipRect && (l.clipRect.style.clip = "auto") } if (e.textpath) { var K = e.textpath.style; i.font && (K.font = i.font), i["font-family"] && (K.fontFamily = '"' + i["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, o) + '"'), i["font-size"] && (K.fontSize = i["font-size"]), i["font-weight"] && (K.fontWeight = i["font-weight"]), i["font-style"] && (K.fontStyle = i["font-style"]) } "arrow-start" in i && A(t, i["arrow-start"]), "arrow-end" in i && A(t, i["arrow-end"], 1); if (i.opacity != null || i["stroke-width"] != null || i.fill != null || i.src != null || i.stroke != null || i["stroke-width"] != null || i["stroke-opacity"] != null || i["fill-opacity"] != null || i["stroke-dasharray"] != null || i["stroke-miterlimit"] != null || i["stroke-linejoin"] != null || i["stroke-linecap"] != null) { var L = l.getElementsByTagName(j), M = !1; L = L && L[0], !L && (M = L = F(j)), e.type == "image" && i.src && (L.src = i.src), i.fill && (L.on = !0); if (L.on == null || i.fill == "none" || i.fill === null) L.on = !1; if (L.on && i.fill) { var N = c(i.fill).match(a._ISURL); if (N) { L.parentNode == l && l.removeChild(L), L.rotate = !0, L.src = N[1], L.type = "tile"; var O = e.getBBox(1); L.position = O.x + n + O.y, e._.fillpos = [O.x, O.y], a._preload(N[1], function () { e._.fillsize = [this.offsetWidth, this.offsetHeight] }) } else L.color = a.getRGB(i.fill).hex, L.src = o, L.type = "solid", a.getRGB(i.fill).error && (t.type in { circle: 1, ellipse: 1 } || c(i.fill).charAt() != "r") && C(t, i.fill, L) && (m.fill = "none", m.gradient = i.fill, L.rotate = !1) } if ("fill-opacity" in i || "opacity" in i) { var P = ((+m["fill-opacity"] + 1 || 2) - 1) * ((+m.opacity + 1 || 2) - 1) * ((+a.getRGB(i.fill).o + 1 || 2) - 1); P = h(g(P, 0), 1), L.opacity = P, L.src && (L.color = "none") } l.appendChild(L); var Q = l.getElementsByTagName("stroke") && l.getElementsByTagName("stroke")[0], T = !1; !Q && (T = Q = F("stroke")); if (i.stroke && i.stroke != "none" || i["stroke-width"] || i["stroke-opacity"] != null || i["stroke-dasharray"] || i["stroke-miterlimit"] || i["stroke-linejoin"] || i["stroke-linecap"]) Q.on = !0; (i.stroke == "none" || i.stroke === null || Q.on == null || i.stroke == 0 || i["stroke-width"] == 0) && (Q.on = !1); var U = a.getRGB(i.stroke); Q.on && i.stroke && (Q.color = U.hex), P = ((+m["stroke-opacity"] + 1 || 2) - 1) * ((+m.opacity + 1 || 2) - 1) * ((+U.o + 1 || 2) - 1); var V = (d(i["stroke-width"]) || 1) * .75; P = h(g(P, 0), 1), i["stroke-width"] == null && (V = m["stroke-width"]), i["stroke-width"] && (Q.weight = V), V && V < 1 && (P *= V) && (Q.weight = 1), Q.opacity = P, i["stroke-linejoin"] && (Q.joinstyle = i["stroke-linejoin"] || "miter"), Q.miterlimit = i["stroke-miterlimit"] || 8, i["stroke-linecap"] && (Q.endcap = i["stroke-linecap"] == "butt" ? "flat" : i["stroke-linecap"] == "square" ? "square" : "round"); if (i["stroke-dasharray"]) { var W = { "-": "shortdash", ".": "shortdot", "-.": "shortdashdot", "-..": "shortdashdotdot", ". ": "dot", "- ": "dash", "--": "longdash", "- .": "dashdot", "--.": "longdashdot", "--..": "longdashdotdot" }; Q.dashstyle = W[b](i["stroke-dasharray"]) ? W[i["stroke-dasharray"]] : o } T && l.appendChild(Q) } if (t.type == "text") { t.paper.canvas.style.display = o; var X = t.paper.span, Y = 100, Z = m.font && m.font.match(/\d+(?:\.\d*)?(?=px)/); p = X.style, m.font && (p.font = m.font), m["font-family"] && (p.fontFamily = m["font-family"]), m["font-weight"] && (p.fontWeight = m["font-weight"]), m["font-style"] && (p.fontStyle = m["font-style"]), Z = d(m["font-size"] || Z && Z[0]) || 10, p.fontSize = Z * Y + "px", t.textpath.string && (X.innerHTML = c(t.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>")); var $ = X.getBoundingClientRect(); t.W = m.w = ($.right - $.left) / Y, t.H = m.h = ($.bottom - $.top) / Y, t.X = m.x, t.Y = m.y + t.H / 2, ("x" in i || "y" in i) && (t.path.v = a.format("m{0},{1}l{2},{1}", f(m.x * u), f(m.y * u), f(m.x * u) + 1)); var _ = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"]; for (var ba = 0, bb = _.length; ba < bb; ba++)if (_[ba] in i) { t._.dirty = 1; break } switch (m["text-anchor"]) { case "start": t.textpath.style["v-text-align"] = "left", t.bbx = t.W / 2; break; case "end": t.textpath.style["v-text-align"] = "right", t.bbx = -t.W / 2; break; default: t.textpath.style["v-text-align"] = "center", t.bbx = 0 }t.textpath.style["v-text-kern"] = !0 } }, C = function (b, f, g) { b.attrs = b.attrs || {}; var h = b.attrs, i = Math.pow, j, k, l = "linear", m = ".5 .5"; b.attrs.gradient = f, f = c(f).replace(a._radial_gradient, function (a, b, c) { l = "radial", b && c && (b = d(b), c = d(c), i(b - .5, 2) + i(c - .5, 2) > .25 && (c = e.sqrt(.25 - i(b - .5, 2)) * ((c > .5) * 2 - 1) + .5), m = b + n + c); return o }), f = f.split(/\s*\-\s*/); if (l == "linear") { var p = f.shift(); p = -d(p); if (isNaN(p)) return null } var q = a._parseDots(f); if (!q) return null; b = b.shape || b.node; if (q.length) { b.removeChild(g), g.on = !0, g.method = "none", g.color = q[0].color, g.color2 = q[q.length - 1].color; var r = []; for (var s = 0, t = q.length; s < t; s++)q[s].offset && r.push(q[s].offset + n + q[s].color); g.colors = r.length ? r.join() : "0% " + g.color, l == "radial" ? (g.type = "gradientTitle", g.focus = "100%", g.focussize = "0 0", g.focusposition = m, g.angle = 0) : (g.type = "gradient", g.angle = (270 - p) % 360), b.appendChild(g) } return 1 }, D = function (b, c) { this[0] = this.node = b, b.raphael = !0, this.id = a._oid++, b.raphaelid = this.id, this.X = 0, this.Y = 0, this.attrs = {}, this.paper = c, this.matrix = a.matrix(), this._ = { transform: [], sx: 1, sy: 1, dx: 0, dy: 0, deg: 0, dirty: 1, dirtyT: 1 }, !c.bottom && (c.bottom = this), this.prev = c.top, c.top && (c.top.next = this), c.top = this, this.next = null }, E = a.el; D.prototype = E, E.constructor = D, E.transform = function (b) { if (b == null) return this._.transform; var d = this.paper._viewBoxShift, e = d ? "s" + [d.scale, d.scale] + "-1-1t" + [d.dx, d.dy] : o, f; d && (f = b = c(b).replace(/\.{3}|\u2026/g, this._.transform || o)), a._extractTransform(this, e + b); var g = this.matrix.clone(), h = this.skew, i = this.node, j, k = ~c(this.attrs.fill).indexOf("-"), l = !c(this.attrs.fill).indexOf("url("); g.translate(-0.5, -0.5); if (l || k || this.type == "image") { h.matrix = "1 0 0 1", h.offset = "0 0", j = g.split(); if (k && j.noRotation || !j.isSimple) { i.style.filter = g.toFilter(); var m = this.getBBox(), p = this.getBBox(1), q = m.x - p.x, r = m.y - p.y; i.coordorigin = q * -u + n + r * -u, z(this, 1, 1, q, r, 0) } else i.style.filter = o, z(this, j.scalex, j.scaley, j.dx, j.dy, j.rotate) } else i.style.filter = o, h.matrix = c(g), h.offset = g.offset(); f && (this._.transform = f); return this }, E.rotate = function (a, b, e) { if (this.removed) return this; if (a != null) { a = c(a).split(k), a.length - 1 && (b = d(a[1]), e = d(a[2])), a = d(a[0]), e == null && (b = e); if (b == null || e == null) { var f = this.getBBox(1); b = f.x + f.width / 2, e = f.y + f.height / 2 } this._.dirtyT = 1, this.transform(this._.transform.concat([["r", a, b, e]])); return this } }, E.translate = function (a, b) { if (this.removed) return this; a = c(a).split(k), a.length - 1 && (b = d(a[1])), a = d(a[0]) || 0, b = +b || 0, this._.bbox && (this._.bbox.x += a, this._.bbox.y += b), this.transform(this._.transform.concat([["t", a, b]])); return this }, E.scale = function (a, b, e, f) { if (this.removed) return this; a = c(a).split(k), a.length - 1 && (b = d(a[1]), e = d(a[2]), f = d(a[3]), isNaN(e) && (e = null), isNaN(f) && (f = null)), a = d(a[0]), b == null && (b = a), f == null && (e = f); if (e == null || f == null) var g = this.getBBox(1); e = e == null ? g.x + g.width / 2 : e, f = f == null ? g.y + g.height / 2 : f, this.transform(this._.transform.concat([["s", a, b, e, f]])), this._.dirtyT = 1; return this }, E.hide = function () { !this.removed && (this.node.style.display = "none"); return this }, E.show = function () { !this.removed && (this.node.style.display = o); return this }, E._getBBox = function () { if (this.removed) return {}; return { x: this.X + (this.bbx || 0) - this.W / 2, y: this.Y - this.H, width: this.W, height: this.H } }, E.remove = function () { if (!this.removed && !!this.node.parentNode) { this.paper.__set__ && this.paper.__set__.exclude(this), a.eve.unbind("raphael.*.*." + this.id), a._tear(this, this.paper), this.node.parentNode.removeChild(this.node), this.shape && this.shape.parentNode.removeChild(this.shape); for (var b in this) this[b] = typeof this[b] == "function" ? a._removedFactory(b) : null; this.removed = !0 } }, E.attr = function (c, d) { if (this.removed) return this; if (c == null) { var e = {}; for (var f in this.attrs) this.attrs[b](f) && (e[f] = this.attrs[f]); e.gradient && e.fill == "none" && (e.fill = e.gradient) && delete e.gradient, e.transform = this._.transform; return e } if (d == null && a.is(c, "string")) { if (c == j && this.attrs.fill == "none" && this.attrs.gradient) return this.attrs.gradient; var g = c.split(k), h = {}; for (var i = 0, m = g.length; i < m; i++)c = g[i], c in this.attrs ? h[c] = this.attrs[c] : a.is(this.paper.customAttributes[c], "function") ? h[c] = this.paper.customAttributes[c].def : h[c] = a._availableAttrs[c]; return m - 1 ? h : h[g[0]] } if (this.attrs && d == null && a.is(c, "array")) { h = {}; for (i = 0, m = c.length; i < m; i++)h[c[i]] = this.attr(c[i]); return h } var n; d != null && (n = {}, n[c] = d), d == null && a.is(c, "object") && (n = c); for (var o in n) l("raphael.attr." + o + "." + this.id, this, n[o]); if (n) { for (o in this.paper.customAttributes) if (this.paper.customAttributes[b](o) && n[b](o) && a.is(this.paper.customAttributes[o], "function")) { var p = this.paper.customAttributes[o].apply(this, [].concat(n[o])); this.attrs[o] = n[o]; for (var q in p) p[b](q) && (n[q] = p[q]) } n.text && this.type == "text" && (this.textpath.string = n.text), B(this, n) } return this }, E.toFront = function () { !this.removed && this.node.parentNode.appendChild(this.node), this.paper && this.paper.top != this && a._tofront(this, this.paper); return this }, E.toBack = function () { if (this.removed) return this; this.node.parentNode.firstChild != this.node && (this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild), a._toback(this, this.paper)); return this }, E.insertAfter = function (b) { if (this.removed) return this; b.constructor == a.st.constructor && (b = b[b.length - 1]), b.node.nextSibling ? b.node.parentNode.insertBefore(this.node, b.node.nextSibling) : b.node.parentNode.appendChild(this.node), a._insertafter(this, b, this.paper); return this }, E.insertBefore = function (b) { if (this.removed) return this; b.constructor == a.st.constructor && (b = b[0]), b.node.parentNode.insertBefore(this.node, b.node), a._insertbefore(this, b, this.paper); return this }, E.blur = function (b) { var c = this.node.runtimeStyle, d = c.filter; d = d.replace(r, o), +b !== 0 ? (this.attrs.blur = b, c.filter = d + n + m + ".Blur(pixelradius=" + (+b || 1.5) + ")", c.margin = a.format("-{0}px 0 0 -{0}px", f(+b || 1.5))) : (c.filter = d, c.margin = 0, delete this.attrs.blur) }, a._engine.path = function (a, b) { var c = F("shape"); c.style.cssText = t, c.coordsize = u + n + u, c.coordorigin = b.coordorigin; var d = new D(c, b), e = { fill: "none", stroke: "#000" }; a && (e.path = a), d.type = "path", d.path = [], d.Path = o, B(d, e), b.canvas.appendChild(c); var f = F("skew"); f.on = !0, c.appendChild(f), d.skew = f, d.transform(o); return d }, a._engine.rect = function (b, c, d, e, f, g) { var h = a._rectPath(c, d, e, f, g), i = b.path(h), j = i.attrs; i.X = j.x = c, i.Y = j.y = d, i.W = j.width = e, i.H = j.height = f, j.r = g, j.path = h, i.type = "rect"; return i }, a._engine.ellipse = function (a, b, c, d, e) { var f = a.path(), g = f.attrs; f.X = b - d, f.Y = c - e, f.W = d * 2, f.H = e * 2, f.type = "ellipse", B(f, { cx: b, cy: c, rx: d, ry: e }); return f }, a._engine.circle = function (a, b, c, d) { var e = a.path(), f = e.attrs; e.X = b - d, e.Y = c - d, e.W = e.H = d * 2, e.type = "circle", B(e, { cx: b, cy: c, r: d }); return e }, a._engine.image = function (b, c, d, e, f, g) { var h = a._rectPath(d, e, f, g), i = b.path(h).attr({ stroke: "none" }), k = i.attrs, l = i.node, m = l.getElementsByTagName(j)[0]; k.src = c, i.X = k.x = d, i.Y = k.y = e, i.W = k.width = f, i.H = k.height = g, k.path = h, i.type = "image", m.parentNode == l && l.removeChild(m), m.rotate = !0, m.src = c, m.type = "tile", i._.fillpos = [d, e], i._.fillsize = [f, g], l.appendChild(m), z(i, 1, 1, 0, 0, 0); return i }, a._engine.text = function (b, d, e, g) { var h = F("shape"), i = F("path"), j = F("textpath"); d = d || 0, e = e || 0, g = g || "", i.v = a.format("m{0},{1}l{2},{1}", f(d * u), f(e * u), f(d * u) + 1), i.textpathok = !0, j.string = c(g), j.on = !0, h.style.cssText = t, h.coordsize = u + n + u, h.coordorigin = "0 0"; var k = new D(h, b), l = { fill: "#000", stroke: "none", font: a._availableAttrs.font, text: g }; k.shape = h, k.path = i, k.textpath = j, k.type = "text", k.attrs.text = c(g), k.attrs.x = d, k.attrs.y = e, k.attrs.w = 1, k.attrs.h = 1, B(k, l), h.appendChild(j), h.appendChild(i), b.canvas.appendChild(h); var m = F("skew"); m.on = !0, h.appendChild(m), k.skew = m, k.transform(o); return k }, a._engine.setSize = function (b, c) { var d = this.canvas.style; this.width = b, this.height = c, b == +b && (b += "px"), c == +c && (c += "px"), d.width = b, d.height = c, d.clip = "rect(0 " + b + " " + c + " 0)", this._viewBox && a._engine.setViewBox.apply(this, this._viewBox); return this }, a._engine.setViewBox = function (b, c, d, e, f) { a.eve("raphael.setViewBox", this, this._viewBox, [b, c, d, e, f]); var h = this.width, i = this.height, j = 1 / g(d / h, e / i), k, l; f && (k = i / e, l = h / d, d * k < h && (b -= (h - d * k) / 2 / k), e * l < i && (c -= (i - e * l) / 2 / l)), this._viewBox = [b, c, d, e, !!f], this._viewBoxShift = { dx: -b, dy: -c, scale: j }, this.forEach(function (a) { a.transform("...") }); return this }; var F; a._engine.initWin = function (a) { var b = a.document; b.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)"); try { !b.namespaces.rvml && b.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"), F = function (a) { return b.createElement("<rvml:" + a + ' class="rvml">') } } catch (c) { F = function (a) { return b.createElement("<" + a + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">') } } }, a._engine.initWin(a._g.win), a._engine.create = function () { var b = a._getContainer.apply(0, arguments), c = b.container, d = b.height, e, f = b.width, g = b.x, h = b.y; if (!c) throw new Error("VML container not found."); var i = new a._Paper, j = i.canvas = a._g.doc.createElement("div"), k = j.style; g = g || 0, h = h || 0, f = f || 512, d = d || 342, i.width = f, i.height = d, f == +f && (f += "px"), d == +d && (d += "px"), i.coordsize = u * 1e3 + n + u * 1e3, i.coordorigin = "0 0", i.span = a._g.doc.createElement("span"), i.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;", j.appendChild(i.span), k.cssText = a.format("top:0;left:0;width:{0};height:{1};display:inline-block;clip:rect(0 {0} {1} 0);overflow:hidden", f, d), c == 1 ? (a._g.doc.body.appendChild(j), k.left = g + "px", k.top = h + "px", k.position = "absolute") : c.firstChild ? c.insertBefore(j, c.firstChild) : c.appendChild(j), i.renderfix = function () { }; return i }, a.prototype.clear = function () { a.eve("raphael.clear", this), this.canvas.innerHTML = o, this.span = a._g.doc.createElement("span"), this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;", this.canvas.appendChild(this.span), this.bottom = this.top = null }, a.prototype.remove = function () { a.eve("raphael.remove", this), this.canvas.parentNode.removeChild(this.canvas); for (var b in this) this[b] = typeof this[b] == "function" ? a._removedFactory(b) : null; return !0 }; var G = a.st; for (var H in E) E[b](H) && !G[b](H) && (G[H] = function (a) { return function () { var b = arguments; return this.forEach(function (c) { c[a].apply(c, b) }) } }(H)) }(window.Raphael);

    const $dropdown = $("#MainMenu .dropdown");
    const $dropdownToggle = $("#MainMenu .dropdown-toggle");
    const $dropdownMenu = $("MainMenu  .dropdown-menu");
    const showClass = "open";

    $('#MainMenu .dropdown a').click(function (e) {
        if (!$(this).parent().hasClass('open') & $(this).parent().hasClass('dropdown')) {
            e.preventDefault();
            $(this).parent().addClass(showClass);
            $(this).parent().find($dropdownToggle).attr("aria-expanded", "true");
            $(this).parent().find($dropdownMenu).addClass(showClass);
        }
    });

    $(window).on("load resize", function () {
        if (this.matchMedia("(min-width: 768px)").matches) {
            $dropdown.hover(
                function () {
                    const $this = $(this);
                    $this.addClass(showClass);
                    $this.find($dropdownToggle).attr("aria-expanded", "true");
                    $this.find($dropdownMenu).addClass(showClass);
                },
                function () {
                    const $this = $(this);
                    $this.removeClass(showClass);
                    $this.find($dropdownToggle).attr("aria-expanded", "false");
                    $this.find($dropdownMenu).removeClass(showClass);
                }
            );
        } else {
            $dropdown.off("mouseenter mouseleave");
        }
    });

    $('#MainMenu .dropdown-toggle').on('click', function () {
        window.location.href = $(this).attr('href');
    });

;
/**
 * Mania Exchange (www.mania-exchange.com)
 * 2010 - 2012
 * Forzyy
 */

var ManiaExchange = Class.create(true);

// MX namespace. jQuery extension.
$.mx = {
  version: "1.0.0.0",
  common: {
    loadingIndicator:
      "http://images.mania-exchange.com/default/ajax-loader3.gif"
  }
};

// Menu stuff
var timeout = 500;
var closetimer = 0;
var ddmenuitem = 0;
var ddmenuitemh = 0;

function mpxmenu_open() {
  mpxmenu_canceltimer();
  mpxmenu_close(true);
  if (
    ddmenuitem !== 0 &&
    ddmenuitem !== null &&
    ddmenuitem.text() ===
      $(this)
        .find("ul")
        .eq(0)
        .text()
  ) {
    // Just instantly show.
    $(this)
      .find("ul")
      .eq(0)
      .stop()
      .css("opacity", 1)
      .css("height", ddmenuitemh)
      .slideDown(0);
  } else {
    ddmenuitem = $(this)
      .find("ul")
      .eq(0)
      .stop();
    ddmenuitemh = ddmenuitem.height();
    ddmenuitem
      .css("opacity", 0)
      .slideDown(150)
      .animate(
        { opacity: 1 },
        {
          queue: false,
          duration: 150
        }
      );
  }
}

function mpxmenu_touched() {
  return false;
}

function mpxmenu_close(slow) {
  if (ddmenuitem) {
    var t = typeof slow === "undefined" ? 350 : 60;
    var ddmenuitemht = ddmenuitemh;
    ddmenuitem
      .stop()
      .slideUp(t)
      .animate(
        { opacity: 0 },
        {
          queue: false,
          duration: t
        }
      );
  }
}

function mpxmenu_timer() {
  closetimer = window.setTimeout(mpxmenu_close, timeout);
}

function mpxmenu_canceltimer() {
  if (closetimer) {
    window.clearTimeout(closetimer);
    closetimer = null;
  }
}

var $qle = null;

function quicklink_show(e) {
  $el = $(this);
  if ($qle === null) {
    $qle = $(
      '<div class="ly-quicklink-box"><span><strong>Page link:</strong> (Ctrl+C to copy)</span> <span style="float:right;"><a href="javascript:quicklink_hide();">Close</a></span> <input type="text" id="quicklink_link" class="form-niceinput expand-max"/></div>'
    ).hide();
    $("body").append($qle);
    $qle.click(function(e) {
      e.stopPropagation();
    });
  }

  $qle.css({
    left: $el.offset().left - $qle.width() + $el.outerWidth(),
    top: $el.offset().top + $el.outerHeight()
  });

  var targetLink = $el.attr("qltarget");
  if (!targetLink) targetLink = document.URL;

  $("#quicklink_link").val(targetLink);

  // Prevent autoclose...
  setTimeout(function() {
    $qle.fadeIn(300);
    $("#quicklink_link").select();
  }, 1);

  e.stopPropagation();
  return false;
}

function quicklink_hide() {
  if ($qle !== null) $qle.fadeOut();
}

function getUserTooltip(userid) {
  return $.ajax({
    url: "/api/users/get_tooltip/" + userid,
    dataType: "html"
  }).responseText;
}

if (!window.location.origin)
  window.location.origin =
    window.location.protocol + "//" + window.location.host;

$(document).ready(function() {
  $("#mpxtoolbar > li").bind("touchend", mpxmenu_touched);

  $("#mpxmenu > li").bind("mouseover", mpxmenu_open);
  $("#mpxmenu > li").bind("mouseout", mpxmenu_timer);


  $("#mpxtoolbar > li").bind("mouseover", mpxmenu_open);
    $("#mpxtoolbar > li").bind("mouseout", mpxmenu_timer);

    $('.dropdown-toggle').click(function (e) {
        e.preventDefault();
        setTimeout($.proxy(function () {
            if ('ontouchstart' in document.documentElement) {
                $(this).siblings('.dropdown-backdrop').off().remove();
            }
        }, this), 0);
    });

  $(".quicklink").click(quicklink_show);

  $(".quicklink").each(function(i) {
    if ($(this).attr("href") === "javascript:;")
      $(this).attr("href", document.URL);
  });

    for (let timeelem of document.querySelectorAll('.time')) {
        timeelem.textContent = formatTime(timeelem.getAttribute('data-time'), false);
        timeelem.title = formatTime(timeelem.getAttribute('data-time'), true);
    }

    for (let timeelem of document.querySelectorAll('.time-d')) {
        timeelem.textContent = formatDate(timeelem.getAttribute('data-time'));
        timeelem.title = formatTime(timeelem.getAttribute('data-time'), true);
    }

  $(".achievement").mouseover(function() {
      var width =
        $(this)
          .find("span")
          .width() + 35;
      $(this)
        .find(".achievement-Text")
        .css({
          "max-width": width + "px",
          "padding-left": "22px"
        });
    })
    .mouseleave(function() {
      $(this)
        .find(".achievement-Text")
        .css({ "max-width": "", "padding-left": "" });
    });

    /*$('.windowv2-header').each(function (index) {
        if (!$(this).parent().children('.windowv2-content').first().attr('id')) {
            $(this).data('toggle', 'collapse');
            $(this).data('target', '#clps-' + index);
            $(this).attr('id', '#clpsh-' + index);
            $(this).attr('aria-controls', '#clps-' + index);
            $(this).attr('aria-expanded', 'true');
            $(this).attr('role', 'button');
            $(this).parent().children('.windowv2-content').first().addClass('collapse');
            //$(this).parent().children('.windowv2-content').first().addClass('show');
            $(this).parent().children('.windowv2-content').first().attr('id', '#clps-' + index);
            $(this).parent().children('.windowv2-content').first().attr('aria-labelledby', 'clpsh-' + index);
            $(this).parent().children('.windowv2-content').first().collapse();
        }
    });*/

  $(".confirm").click(function() {
    var msg = $(this).data("confirm-msg");
    var url = $(this).data("confirm-href");
    var funct = $(this).data("confirm-funct");
    if (confirm(msg)) {
      $.ajax({
        type: "GET",
        dataType: "json",
        global: false,
        url: url,
        success: function(data) {
          if (data.Success == true) {
            if (data.Message && data.Message !== "Success!")
              alert(data.Message);
            else alert("Action performed successfully.");
            if (data.returnURL != null) window.location.href = data.returnURL;
            else if (data.returnURL == "none")
              if (funct == "removeancestor")
                $(this)
                  .closest(".list-group-item ")
                  .remove();
              else return;
            else location.reload();
          } else {
            alert("Error: " + data.Message);
            console.log(data);
          }
        },
        error: function(data) {
          console.log(data);
        }
      });
    }
  });

  // User Tooltips

  /*$('.userlink-popover').each(function (i) {
        $(this).popover({
            html: true,
            trigger: 'manual',
            placement: 'right',
            content: getUserTooltip($(this).data('userid'))
        }).on("mouseover", function () {
            var _this = this;
            $('.userlink-popover').not(this).each(function () {
                $(this).popover('hide');
            });
            $(this).popover("show");
            $(this).siblings(".popover").on("mouseleave", function () {
                $(this).popover('hide');
            });
        }).on("mouseout", function () {
            var _this = this;
            setTimeout(function () {
                if (!$(".popover:hover").length) {
                    $(_this).popover("hide");
                }
            }, 100);
        });
    });*/

  $(document).on("mouseleave", ".popover-content", function() {
    $(".userlink-popover").popover("hide");
  });

  // Stop links from not working inside onClick Event.
  $(".WindowNewsHeader a").click(function(e) {
    e.stopPropagation();
  });

  // Load messages and notifications.
  $.get("/api/getuserdata", function(json) {
    if (typeof json === "object") {
      if (json.newMessages) {
        // New message, flash icon
        var icon = $("#privateMessageIcon");
        icon.css("color", "yellow").attr("title", "Unread messages!");
        var state = false;
        var switcher = function() {
          state = !state;
          icon.attr("class", state ? "icon-envelope" : "icon-envelope-alt");
          setTimeout(switcher, 500);
        };
        switcher();
      }

      if (json.notificationCount > 0) {
        // Notifications
        var notifs = $("#notificationCount");
        notifs.html(json.notificationCount).css("display", "inline-block");
        var width = notifs.width();
        notifs.css("opacity", 0).animate({
          padding: "1px 5px 1px 4px",
          "margin-left": "8px",
          opacity: 1
        });
      }
    }
  });

  // Init search
  var searchBar = $("#userbar-search");
  var qList = $("#userbar-search-ql");
  var qListItems = $("#userbar-search-quicks");
  var activeQItem = $(".userbar-search-quicks-active").first();
  var qListSpecialItem = $("#userbar-search-special")
    .first()
    .hide();
  var isQListVisible = false;
  var searchTerm = "";

  var searchSpecialTerms = {
    canyon: {
      url: "tracksearch2?environments=1",
      display: 'Search tracks in the "<b>Canyon</b>" environment'
    },
    stadium: {
      url: "tracksearch2?environments=2",
      display: 'Search tracks in the "<b>Stadium</b>" environment'
    },
    valley: {
      url: "tracksearch2?environments=3",
      display: 'Search tracks in the "<b>Valley</b>" environment'
    },
    lagoon: {
      url: "tracksearch2?environments=4",
      display: 'Search tracks in the "<b>Lagoon</b>" environment'
    }
  };

  function animateQList(animateIn) {
    if ((isQListVisible && animateIn) || (!isQListVisible && !animateIn))
      return;

    isQListVisible = animateIn;
    if (!animateIn) {
      qList.animate(
        { opacity: 0 },
        {
          duration: 120,
          queue: false,
          complete: function() {
            qList.hide();
          }
        }
      );
    } else {
      qList
        .show()
        .css("opacity", 0)
        .animate({ opacity: 1 }, { duration: 120, queue: false });
    }
  }

  function swapQItem(delta) {
    var newActive = null;
    if (delta == -1) newActive = activeQItem.prev();
    else if (delta == 1) newActive = activeQItem.next();

    if (newActive.is(":visible")) setActive(newActive);
  }

  function setActive(newActive) {
    if (newActive && newActive.length > 0) {
      activeQItem.removeClass("userbar-search-quicks-active");
      activeQItem = newActive;
      activeQItem.addClass("userbar-search-quicks-active");
    }
  }

  function setSearchTerm(newVal) {
    searchTerm = newVal || "";
    $(".userbar-search-term").text(searchTerm);

    // Special term handling
    var specialTerm = searchSpecialTerms[searchTerm.trim().toLowerCase()];
    var specialVisible = qListSpecialItem.is(":visible");
    if (specialTerm === undefined && specialVisible) {
      // Hide and unfocus special
      if (qListSpecialItem.get(0) == activeQItem.get(0))
        setActive(activeQItem.next());

      qListSpecialItem.hide().detach();
      qListSpecialItem.attr("data-sq", "");
    } else if (specialTerm !== undefined && !specialVisible) {
      // Show and set special
      qListItems.prepend(qListSpecialItem);
      qListSpecialItem.attr("data-sq", specialTerm.url);
      qListSpecialItem.html(specialTerm.display);
      qListSpecialItem.show();
    }
  }

  function navigateToActive() {
    var url =
      window.location.origin +
      "/" +
      activeQItem
        .attr("data-sq")
        .replace("{q}", encodeURIComponent(searchTerm));
    window.location.href = url;
  }

  searchBar
    .on("input", function() {
      setSearchTerm(searchBar.val());

      // Set width
      if ($(window).width() >= 768) {
        var width = $("#userbar-search-lower").width();
        qList.css("width", width + 5 + "px");
      } else if ($(window).width() >= 440) {
        qList.css({
          "margin-left": "56px",
          width: $(window).width() - 120 + "px"
        });
      } else {
        qList.css({ "margin-left": "-4px", width: $(window).width() + "px" });
      }

      // Animate search qList in/out
      animateQList(searchTerm != "");
    })
    .on("blur", function() {
      animateQList(false);
    })
    .on("focus", function() {
      animateQList(searchTerm != "");
    })
    .keydown(function(e) {
      if (e.keyCode == 38) {
        // up
        swapQItem(-1);
      } else if (e.keyCode == 40) {
        // down
        swapQItem(1);
      } else if (e.keyCode == 13) {
        // enter
        navigateToActive();
      } else {
        return; // do not prevent default
      }
      e.preventDefault();
    });

  qListItems.children().each(function() {
    var el = $(this);
    el.mouseenter(function() {
      setActive(el);
    });
    el.click(function(e) {
      setActive(el);
      navigateToActive();
      e.preventDefault();
    });
  });

  qListSpecialItem.detach();

  $(document).keydown(function(e) {
    // Don't handle shortcuts if the focus is on an input element
    if ($(document.activeElement).is(":input")) return;
    if ($(document.activeElement).is(".CodeMirror-code")) return;

    if (e.keyCode == 83) {
      // 's'
      // Set focus
      searchBar.focus();

      // Reenter value to set caret to the end
      var currentValue = searchBar.val();
      searchBar.val("");
      searchBar.val(currentValue);

      e.preventDefault();
    }
  });

  // Init already filled searchterm (some browsers remember it for some reason)
  setTimeout(function() {
    setSearchTerm(searchBar.val());
  }, 50);

  // Sitenav/Usernav dropdown (rewritten & partially hardcoded)
  var lock = false;
  $("#userbar-sitenav-toggle, #userbar-usernav-toggle").on("click", function(
    e
  ) {
    if (
      (e.currentTarget.id == "userbar-usernav-toggle" &&
        $("#userbar-sitenav-toggle").hasClass("userbar-sitetitle-active")) ||
      (e.currentTarget.id == "userbar-sitenav-toggle" &&
        $("#userbar-usernav-toggle").hasClass("userbar-sitetitle-active"))
    ) {
      $("#userbar-sitenav, #userbar-usernav").fadeOut({
        duration: 250,
        queue: true
      });
      $("#userbar-sitenav-toggle, #userbar-usernav-toggle").removeClass(
        "userbar-sitetitle-active"
      );
    }
    lock = true;
    $("#" + e.currentTarget.id.replace("-toggle", "")).fadeIn({
      duration: 120,
      queue: true,
      complete: function() {
        lock = false;
      }
    });
    $("#" + e.currentTarget.id).addClass("userbar-sitetitle-active");
  });
  $(document).click(function() {
    if (!lock) {
      $("#userbar-sitenav, #userbar-usernav").fadeOut({
        duration: 120,
        queue: true
      });
      $("#userbar-sitenav-toggle, #userbar-usernav-toggle").removeClass(
        "userbar-sitetitle-active"
      );
    }
  });

  initTimeAgo();
  initVideos();
});

document.onclick = function() {
  mpxmenu_close(true);
  quicklink_hide();
};

// TimeAgo
function initTimeAgo() {
  $("abbr.timeago").timeago();
}

function PlayLaterAdd(id) {
  request = $.ajax({
    type: "POST",
    url: "/playlater/add/" + id,
    contentType: "application/json; charset=utf-8",
    success: function() {},
    error: function(data) {}
  });
  togglePlayLaterIcon(id, true);
}

function PlayLaterRemove(id) {
  request = $.ajax({
    type: "POST",
    url: "/playlater/remove/" + id,
    contentType: "application/json; charset=utf-8",
    success: function() {},
    error: function(data) {}
  });
  togglePlayLaterIcon(id, false);
}

function togglePlayLaterIcon(tid, add) {
  var x = document.getElementById("tr-" + tid);
  if (x) {
    var cname = add ? "far fa-bookmark" : "fas fa-bookmark";
    var ncname = add ? "fas fa-bookmark" : "far fa-bookmark";
    var ntitle = add
      ? "Remove this track from PlayLater"
      : "Play this track later";
    x.setAttribute(
      "onclick",
      add ? "PlayLaterRemove(" + tid + ")" : "PlayLaterAdd(" + tid + ")"
    );
    $(x)
      .children()
      .removeClass(cname);
    $(x)
      .children()
      .addClass(ncname);
    $(x).attr("original-title", ntitle);
  }
}

var loadedApi = false;
var ytApiFinishedLoading = false;
function initVideos() {
  $("div.bb-youtube").each(function() {
    if (!loadedApi) {
      loadedApi = true;
      var tag = document.createElement("script");
      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName("script")[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
      return;
    }
    if (!ytApiFinishedLoading || $(this).hasClass("initialized")) return;

    var vidId = $(this).attr("yt-video-id");
    $(this).addClass("initialized");
    var el = $(this);
    var button = $(this).find(".bb-youtube-play");
    var image = $(this).find("img");
    var vid = $(this).find(".bb-youtube-video")[0];
    var overlay = $(this).find(".bb-youtube-overlay");
    var overlayShown = true;
    var initiatedLoad = false;
    /*$.getJSON('/api/youtubename/' + encodeURIComponent(vidId), function (data) {
            if (data.found) {
                overlay.html("<a href=\"https://www.youtube.com/watch?v=" + vidId + "\" target=\"blank\"></a>");
                overlay.find('a').text(data.videoName);
            } else {
                overlay.text(data.videoName);
                button.remove();
            }
        });*/

    button.click(function() {
      if (initiatedLoad) return;

      var player = new YT.Player(vid, {
        height: image.height(),
        width: image.width(),
        videoId: vidId,
        suggestedQuality: "hd720",
        events: {
          onReady: function(e) {
            e.target.playVideo();
          },
          onStateChange: function(e) {
            if (e.data == YT.PlayerState.PLAYING && overlayShown) {
              image.fadeOut(300);
              button.fadeOut(300, function() {
                button.remove();
              });
              overlay.fadeOut(300);

              overlayShown = false;
            }
          }
        }
      });

      image.addClass("bb-youtube-imgoverlay");
      button.addClass("bb-youtube-loading");
      button.find("button").fadeOut(200);
      initiatedLoad = true;
    });
  });
}

var timerInterval;

function onYouTubeIframeAPIReady() {
  ytApiFinishedLoading = true;
  initVideos();
}

// Timer / Countdown - by ThaumicTom

function timerStop() {
  clearInterval(timerInterval);
}

function timer(dt, plc, utd) {
  // dt = time, plc = place to place, d = distance, utd = usertimediff cookie

  var _second = 1000;
  var _minute = _second * 60;
  var _hour = _minute * 60;
  var _day = _hour * 24;
  timerInterval = setInterval(function() {
    var d =
      new Date(dt * 1000) - new Date().setHours(new Date().getHours() - utd);
    var days = Math.floor(d / _day);
    var hours = Math.floor((d % _day) / _hour);
    var minutes = Math.floor((d % _hour) / _minute);
    var seconds = Math.floor((d % _minute) / _second);
    if (d < 86400 * 1000) {
      if (d < 0) {
        $(plc).html('<div class="red-text">EXPIRED</div>');
        timerStop();
      } else {
        $(plc).html(
          '<div class="yellow-text">' +
            days +
            "d " +
            hours +
            "h " +
            minutes +
            "m " +
            seconds +
            "s</div>"
        );
      }
    } else {
      $(plc).html(
        "<div>" +
          days +
          "d " +
          hours +
          "h " +
          minutes +
          "m " +
          seconds +
          "s</div>"
      );
    }
  }, 1000);
}

function convertMegabyteToByteBinary(input) {
  return input * 1048576; //Math.Pow(2, 20)
}

function msFormatter(ms) {
  if (ms != -1) {
    var hours = 0;
    var minutes = Math.floor(ms / 60000);
    if (ms >= 3600000) {
      hours = Math.floor(ms / 3600000);
      minutes = minutes - hours * 60;
      if (minutes == 0) {
        minutes = "00";
      }
    }
    var seconds = Math.floor((ms % 60000) / 1000);
    var millis = ms.replace(/^[0-9]+(?=[0-9]{3}$)/g, "");
    return (
      (hours == 0 ? "" : hours + ":") +
      minutes +
      ":" +
      (seconds < 10 ? "0" : "") +
      seconds +
      "." +
      millis
    );
  } else {
    return "N/A";
  }
}

function deformatString(str) {
  return str
    .replace(/\$\$/g, "$\\")
    .replace(
      /\$([a-f0-9]){3}|\$([a-f0-9]){1,2}(?=[^a-f0-9])|\$([lh]\[.*?\]|[g-il-ostwz])/gi,
      ""
    )
    .replace(/\$\\/, "$");
}

/*const elem = document.getElementById('mapfavouritebutton');
const overlay = document.getElementById('overlay');

elem.addEventListener('click', async function () {
    elem.classList.add('disabled');
    const uid = elem.dataset.uid;

    const response = await fetch('/api/maps/setingamefavourite?uid=' + uid, { method: 'POST', headers: { RequestVerificationToken: document.querySelector('input[name="__RequestVerificationToken').value } });
    const result = await response.json();

    if (result.Result === 'OK') {

    }
    if (result.Result === 'AUTH') {
        // centering by https://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
        const y = window.top.outerHeight / 2 + window.top.screenY - (800 / 2);
        const x = window.top.outerWidth / 2 + window.top.screenX - (600 / 2);

        overlay.style.opacity = 0.5;
        overlay.style.pointerEvents = '';
        const popup = window.open('https://account.mania.exchange/account/syncuplayfavorites', 'UPlayFavoritesLink', `width=600,height=800,menubar=no,toolbar=no,top=${y},left=${x}`);

        let openCheckTimer;
        openCheckTimer = window.setInterval(async function () {
            if (popup.closed) {
                window.clearInterval(openCheckTimer);
                overlay.style.opacity = 0;
                overlay.style.pointerEvents = 'none';

                // retry the setfavorite
                const response = await fetch('/api/maps/setingamefavourite?uid=' + uid, { method: 'POST', headers: { RequestVerificationToken: document.querySelector('input[name="__RequestVerificationToken').value } });
                const result = await response.json();
                if (result.Result === 'AUTH') {
                    alert('Could not authenticate, try again.');
                } else if (result.Result !== 'OK') {
                    alert('Failed.');
                }

                elem.classList.remove('disabled');
            }
        }, 100);
        return;
    } else {
        alert('Failed.');
    }

    elem.classList.remove('disabled');
});
*/
/**
 * Initalise Map Upload (ThaumicTom)
 *
 * @param  {number}  fs              - max file size
 * @param  {number}  fsSupporter     - max file size for supporters
 * @param  {number}  fsServersMX     - max file size for servers on MX
 * @param  {number}  fsServersTMX    - mas file size for servers on TMX
 * @param  {boolean} supporterStatus - is Supporter?
 * @param  {boolean} tmxStatus       - is TMX site?
 * @return {void}
 */
function mapUploadInit(
  fs,
  fsSupporter,
  fsServersMX,
  fsServersTMX,
  supporterStatus,
  tmxStatus
) {
  // Constants
  var maxFileSize = fs;
  var maxFileSizeSupporters = fsSupporter;
  var maxFileSizeServersMX = fsServersMX;
  var maxFileSizeServersTMX = fsServersTMX;
  var isSupporter = supporterStatus;
  var isTMX = tmxStatus;
  // Parser
  var form = $(".file-anim");
  var manual = $("#upload_map");
  var file = [],
    dropFile = (isScreen = isMap = multi = false);
  var fileSize;

  manual.on("change", function(e) {
    file = e.target.files[0];
    if (file.name.match(/(\.Map\.Gbx$|\.Challenge\.Gbx$)/gi) !== null) {
      $("#track_file").replaceWith(
        manual.clone().attr({
          id: "track_file",
          name: "track_file",
          accept: ".gbx",
          style: ""
        })
      );
      processFile(file);
    } else if (file.name.match(/(\.jpg$|\.jpeg$|\.png$|\.webp$)/gi) !== null) {
      $("#track_screenshot").replaceWith(
        manual.clone().attr({
          id: "track_screenshot",
          name: "track_screenshot",
          accept: ".jpg,.jpeg,.jpe,.jfif,.exif,.png,.webp",
          style: ""
        })
      );
      processScreen(file);
    } else {
      alert(
        "Wrong format. " +
          file.name.match(/(\..[^\.]*$|\..[^\.]*.gbx$)/gi)[0] +
          " isn't supported."
      );
      return;
    }
    $("#upload_map").removeAttr("value");
  });

  form
    .on("drag dragstart dragend dragover dragenter dragleave drop", function(
      e
    ) {
      e.preventDefault();
      e.stopPropagation();
    })
    .on("dragover dragenter", function() {
      $(".file-anim").addClass("dragover");
    })
    .on("dragleave dragend drop", function() {
      $(".file-anim").removeClass("dragover");
    })
    .on("drop", function(e) {
      dropFile = e.originalEvent.dataTransfer.files;
      if (dropFile.length >= 2) multi = true;
      for (var i = 0; i < (multi ? 2 : 1); i++) {
        if (
          dropFile[i].name.match(/(\.Map\.Gbx$|\.Challenge\.Gbx$)/gi) !== null
        ) {
          if (multi) {
            var dataTransfer = new DataTransfer();
            dataTransfer.items.add(dropFile.item(i));
            document.querySelector("#track_file").files = dataTransfer.files;
          } else {
            document.querySelector("#track_file").files = dropFile;
          }

          $("#map-clip-thumbnail").addClass("clip-path-thumbnail");
          $("#screenshot")
            .removeClass("clip-path-override")
            .hide();
          $("#gbxjs-maperror b,#gbxjs-mapwarn b,#gbxjs-mapsize b").text("");
          $("#gbxjs-maperror,#gbxjs-mapwarn,#gbxjs-mapsize").hide();

          fileSize = dropFile[i].size;

          processFile(dropFile[i]);

          if (
            dropFile.length > 2 ||
            (multi &&
              dropFile[1 - i].name.match(
                /(\.Map\.Gbx$|\.Challenge\.Gbx$)/gi
              ) !== null)
          ) {
            break;
          }
        } else if (
          dropFile[i].name.match(/(\.jpg$|\.jpeg$|\.png$|\.webp$)/gi) !== null
        ) {
          if (multi) {
            var dataTransfer = new DataTransfer();
            dataTransfer.items.add(dropFile.item(i));
            document.querySelector("#track_screenshot").files =
              dataTransfer.files;
          } else {
            document.querySelector("#track_screenshot").files = dropFile;
          }

          $("#gbxjs-screenerror b").text("");
          $("#gbxjs-screenerror").hide();

          processScreen(dropFile[i]);

          if (
            dropFile.length > 2 ||
            (multi &&
              dropFile[1 - i].name.match(
                /(\.jpg$|\.jpeg$|\.png$|\.webp$)/gi
              ) !== null)
          ) {
            break;
          }
        } else {
          alert(
            "Wrong format. " +
              dropFile[i].name.match(/(\..[^\.]*$|\..[^\.]*.gbx$)/gi)[0] +
              " isn't supported."
          );
          return;
        }
        stored = i + 1;
      }
    });

  function processFile(buffer) {
    // gbx.js library by ThaumicTom & BigBang1112 (https://github.com/ThaumicTom/gbx.js)
    new GBX({
      data: buffer,
      thumbnail: "base64",
        onParse: function (e) {
        // check if there is already a map (replace)
        if (isMap) {
          $("#map-clip-thumbnail").addClass("clip-path-thumbnail");
        }

        // clean up
        $(
          "#gbxjs-screenerror, #gbxjs-maperror, #gbxjs-mapwarn, #gbxjs-mapsize"
        ).hide();
        $(".tipsy").remove();

        // display metadata
        $("#gbxjs-mapname").text(e.mapNameD);

        // consider name
        if (e.lightmapVersion >= 8) {
          $("#gbxjs-author").text(e.authorNickname);
        } else if (e.game == "Trackmania 1") {
          $("#gbxjs-author").text(e.mapInfo.author);
        } else {
          $("#gbxjs-author").text(e.authorLogin);
        }

        var envi = e.mapInfo.collection;
        // envi overrides
        if (e.titleUID == "TM2U_Island@adamkooo") {
          envi = "Island";
        } else if (e.titleUID == "TMOneAlpine@unbitn") {
          envi = "Alpine";
        } else if (e.titleUID == "TMOneSpeed@florenzius") {
          envi = "Speed";
        }
        // consider vehicle

        var vehicle = e.xml.match(/<playermodel id="(.*?)"\/>/i);
        if (vehicle !== null) {
          vehicle = vehicle[1].replace(/\.Item\.Gbx$/gi, "");
          if (e.game == "Trackmania®") {
            if (vehicle == "") {
              $("#gbxjs-envi").text(envi + " / " + "CarSport");
            } else {
              $("#gbxjs-envi").text(envi + " / " + vehicle);
            }
          } else {
            if (vehicle == "") {
              $("#gbxjs-envi").text(
                e.mapInfo.collection + " / " + envi + "Car"
              );
            } else {
              $("#gbxjs-envi").text(envi + " / " + vehicle);
            }
          }
        } else {
          $("#gbxjs-envi").text(e.mapInfo.collection);
          $("#gbxjs-envi")
            .parent()
            .attr("original-title", "Environment");
        }

        // shootmania
        if (e.mapInfo.collection == 202) {
            $("#gbxjs-envi").text("Shootmania");
        }

        // consider times
        var authortime = msFormatter(e.authorTime.toString());
        var goldtime = msFormatter(e.goldTime.toString());
        var silvertime = msFormatter(e.silverTime.toString());
        var bronzetime = msFormatter(e.bronzeTime.toString());

        $("#gbxjs-authortime").text(authortime);
        if (e.authorTime != -1) {
          $("#gbxjs-authortime")
            .parent()
            .attr(
              "original-title",
              "<b>All map times:</b><br><span>Author: " +
                authortime +
                "</span><br><span>Gold: " +
                goldtime +
                "</span><br><span>Silver: " +
                silvertime +
                "</span><br><span>Bronze: " +
                bronzetime +
                "</span><br>"
            );
        }

        // returns boolean
        if (e.game != "Trackmania 1") {
          var validated = !!+e.xml.match(/validated="([0-1])"/i)[1];
        }

        // errors - wrong title
        if (e.game == "Trackmania®" && isTMX == false) {
          $("#gbxjs-maperror b").html(
            'ManiaExchange does not support Trackmania® maps. Use <a href="//trackmania.exchange/">TrackmaniaExchange</a> instead.'
          );
            $("#gbxjs-maperror").show();
        } else if (e.game == "ManiaPlanet" && e.mapInfo.collection != "Stadium®" && isTMX == true) {
          $("#gbxjs-maperror b").html(
            'TrackmaniaExchange does not support ManiaPlanet maps. Use <a href="//tm.mania.exchange/">ManiaExchange</a> instead.'
          );
          $("#gbxjs-maperror").show();
        } else if (e.game != "ManiaPlanet" && e.game != "Trackmania®") {
          $("#gbxjs-maperror b").text(
            (isTMX ? "TrackmaniaExchange" : "ManiaExchange") +
              " does not support " +
              e.game +
              " maps."
          );
          $("#gbxjs-maperror").show();
        }
        // errors - other
        else if (e.lightmapVersion == 0) {
          $("#gbxjs-maperror b").html(
            "You must compute lightmaps before uploading this map."
          );
          $("#gbxjs-maperror").show();
        } else if (!validated) {
          $("#gbxjs-maperror b").html(
            "You must validate the map before uploading."
          );
          $("#gbxjs-maperror").show();
        }

        var maxSize = isSupporter
          ? convertMegabyteToByteBinary(maxFileSizeSupporters)
          : convertMegabyteToByteBinary(maxFileSize);
        // errors - filesize
        if (fileSize > maxSize) {
          $("#gbxjs-mapsize b").html(
            "The map file size exceeds the " +
              (isSupporter
                ? maxFileSizeSupporters + " MB"
                : maxFileSize + " MB") +
              " limit."
          );
          $("#gbxjs-mapsize").show();
        } else if (
          fileSize >
          (isTMX
            ? convertMegabyteToByteBinary(maxFileSizeServersTMX)
            : convertMegabyteToByteBinary(maxFileSizeServersMX))
        ) {
          $("#gbxjs-mapwarn b").html(
              "The map file size exceeds the " + (isTMX
                  ? maxFileSizeServersTMX
                  : maxFileSizeServersMX).toString() + "MB limit, it will not be compatible with dedicated servers."
          );
          $("#gbxjs-mapwarn").show();
        }

        $("#map-submit button").removeClass("disabled");
        $("#map-submit, #map-list").show();
        $("#map-btn-group .map-remove").remove();
        $("#map-btn-group").append(
          '<a original-title="Remove map" class="tipsy-n-html btn map-remove" style="background-color: #d9534f; border-color: #d43f3a; color: #fff !important;"><i class="fas fa-minus-circle"></i></a>'
        );
        $(".map-info").css("opacity", "1");

        // map remove
        $(".map-remove").on("click", function() {
          (file = []), (stored = 0), (isScreen = isMap = false);
          $("#track_file, #track_screenshot").removeAttr("value");
          $(".tipsy, .map-remove-screen").remove();
          $("#map-clip-thumbnail").addClass("clip-path-thumbnail");
          $("#map-screen-2, #map-screen-preview").removeAttr("style");
          $("#screenshot, #map-screen-preview")
            .removeClass("clip-path-override")
            .removeAttr("src");
          $("#screenshot, #map-submit, #map-list").hide();
          $(".map-info").css("visiblity", "hidden");
        });
        isMap = true;
      },
      onThumb: function(e) {
        // Testing
        var src = "data:image/jpg;base64," + e;

        var img_src = document.createElement("img");
        document.getElementById("container").appendChild(img_src);

        $("#screenshot").show();

        flip(src, function(res) {
          $("#screenshot")
            .attr("src", res)
            .addClass("clip-path-override");
          $("#map-clip-thumbnail").removeClass("clip-path-thumbnail");
        });

        function flip(src, callback) {
          var image = new Image();
          image.src = src;
          image.onload = function() {
            var canvas = document.createElement("canvas");
            canvas.width = image.height;
            canvas.height = image.width;
            canvas.style.position = "absolute";
            var ctx = canvas.getContext("2d");
            ctx.translate(0, image.height);
            ctx.scale(1, -1);
            ctx.drawImage(image, 0, 0);
            callback(canvas.toDataURL());
          };
        }
      }
    });
  }

  function processScreen(file) {
    if (isScreen == true) {
      $(".tipsy").remove();
      $("#track_screenshot").removeAttr("value");
      $("#map-screen-2 img")
        .removeClass("clip-path-override")
        .css("margin-right", "-10px");
    } else {
      $(".map-preview").css("grid-template-columns", "auto auto 1fr 43px");
      $(".map-remove-screen").remove();
      $("#map-screen-2 img").css("margin-left", "0px");
    }

    if (isMap == false) {
      $("#map-submit button").addClass("disabled");
      $("#map-submit, #map-list").show();
      $(".map-remove").remove();
      $(".map-info").css("opacity", "0");
      $("#map-screen-2 img").css("margin-left", "0px");
    }

    var reader = new FileReader();

    reader.onload = function(e) {
      var img = new Image();
      var height, width;

      img.onload = function() {
        width = img.width;
        height = img.height;

        // errors

        if (file.size > 512000) {
          $("#gbxjs-screenerror b").text(
            "The screenshot file size exceeds the 500KB limit."
          );
          $("#gbxjs-screenerror").show();
        } else {
          if (!(2 > width / height && width / height > 0.65)) {
            $("#gbxjs-screenerror b").text(
              "The ratio of the screenshot is exceeding the limit."
            );
            $("#gbxjs-screenerror").show();
          }

          $("#map-screen-preview")
            .attr("src", e.target.result)
            .addClass("clip-path-override")
            .css({ height: "128px", width: "auto" });

          $("#map-screen-2 img").css("margin-left", "10px");

          if (width / height <= 2) {
            $("#map-screen-2").css(
              "margin-right",
              width / height * -128 + "px"
            );
            $("#map-screen-2 img").css(
              "margin-right",
              width / height * 128 + "px"
            );
          }
          // Make image smaller if bigger than 2:1
          else {
            $("#map-screen-preview").css({ height: "auto", width: "256px" });
            $("#map-screen-2").css("margin-right", -256 + "px");
            $("#map-screen-2 img").css("margin-right", 256 + "px");
          }

          if (isScreen == false) {
            $(".map-preview").css(
              "grid-template-columns",
              "auto auto 1fr 86px"
            );
            $(".map-preview .btn-group").prepend(
              '<a original-title="Remove screenshot" class="tipsy-n-html btn map-remove-screen" style="background-color: #d9534f; border-color: #d43f3a; color: #fff !important;"><i class="fas fa-image"></i></a>'
            );
            $(".map-remove-screen").on("click", function() {
              $(".map-preview").css(
                "grid-template-columns",
                "auto auto 1fr 43px"
              );
              $(".map-remove-screen, .tipsy").remove();
              $("#track_screenshot").removeAttr("value");
              $("#map-screen-2 img")
                .removeClass("clip-path-override")
                .css("margin-right", "-10px");
              if (isMap == false) {
                (file = []), (stored = 0), (isScreen = isMap = false);
                $("#track_file, #track_screenshot").removeAttr("value");
                $(".tipsy, .map-remove-screen").remove();
                $("#map-clip-thumbnail").addClass("clip-path-thumbnail");
                $("#map-screen-2, #map-screen-preview").removeAttr("style");
                $("#screenshot, #map-screen-preview")
                  .removeClass("clip-path-override")
                  .removeAttr("src");
                $("#screenshot, #map-submit, #map-list").hide();
              }
              isScreen = false;
            });

            isScreen = true;
          }
        }
      };

      img.src = e.target.result;
    };

    reader.readAsDataURL(file);
  }
}

/**
 * Initalise Replay Upload (ThaumicTom)
 *
 * @param  {boolean} tmxStatus       - is TMX site?
 * @return {void}
 */
function replayUploadInit(tmxStatus) {
  // Constants
  var isTMX = tmxStatus;
  // Parser
  var form = $(".file-anim");
  var manual = $("#upload_replayfile");
  var file = [],
    dropFile = (isScreen = isMap = multi = false);

  manual.on("change", function(e) {
    file = e.target.files[0];
    if (file.name.match(/\.Replay\.Gbx$/gi) !== null) {
      $("#replay_file").replaceWith(
        manual.clone().attr({
          id: "replay_file",
          name: "replay_file",
          accept: ".gbx",
          style: ""
        })
      );
      processFile(file);
    } else {
      alert(
        "Wrong format. " +
          file.name.match(/(\..[^\.]*$|\..[^\.]*.gbx$)/gi)[0] +
          " isn't supported."
      );
      return;
    }
    $("#upload_replayfile").removeAttr("value");
  });

  form
    .on("drag dragstart dragend dragover dragenter dragleave drop", function(
      e
    ) {
      e.preventDefault();
      e.stopPropagation();
    })
    .on("dragover dragenter", function() {
      $(".file-anim").addClass("dragover");
    })
    .on("dragleave dragend drop", function() {
      $(".file-anim").removeClass("dragover");
    })
    .on("drop", function(e) {
      dropFile = e.originalEvent.dataTransfer.files;
      if (dropFile[0].name.match(/(\.Replay\.Gbx$)/gi) !== null) {
        document.querySelector("#replay_file").files = dropFile;

        $("#gbxjs-replayerror b").text("");
        $("#gbxjs-replayerror").hide();

        processFile(dropFile[0]);
      } else {
        alert(
          "Wrong format. " +
            dropFile[0].name.match(/(\..[^\.]*$|\..[^\.]*.gbx$)/gi)[0] +
            " isn't supported."
        );
        return;
      }
    });

  function processFile(buffer) {
    // gbx.js library by ThaumicTom & BigBang1112 (https://github.com/ThaumicTom/gbx.js)
    new GBX({
      data: buffer,
      onParse: function(e) {
        if (Object.keys(e).length > 0) {
          // clean up
          $("#gbxjs-information").show();
          $("#gbxjs-replayerror, #gbxjs-replaywarn").hide();
          $(".tipsy").remove();

          // display metadata
          $("#gbxjs-mapname").text(
            deformatString(e.xml.match(/name="(.*?)"/i)[1])
          );
          $("#gbxjs-time").text(msFormatter(e.time.toString()));

          // consider name
          if (e.game == "Trackmania®") {
            $("#gbxjs-driver").text(e.driverNickname);
          } else if (e.game == "Trackmania Turbo") {
            $("#gbxjs-driver").text(deformatString(e.driverNickname));
          } else {
            $("#gbxjs-driver").text(e.authorLogin);
          }

          // enivronment
          var envi = e.mapInfo.collection;
          // envi overrides
          if (e.titleUID == "TM2U_Island@adamkooo") {
            envi = "Island";
          } else if (e.titleUID == "TMOneAlpine@unbitn") {
            envi = "Alpine";
          } else if (e.titleUID == "TMOneSpeed@florenzius") {
            envi = "Speed";
          }

          // car
          var vehicle = e.xml.match(/<playermodel id="(.*?)"\/>/i);
          if (vehicle !== null) {
            vehicle = vehicle[1].replace(/\.Item\.Gbx$/gi, "");
            if (e.game == "Trackmania®") {
              if (vehicle == "") {
                $("#gbxjs-envi").text(envi + " / " + "CarSport");
              } else {
                $("#gbxjs-envi").text(envi + " / " + vehicle);
              }
            } else {
              if (vehicle == "") {
                $("#gbxjs-envi").text(
                  e.mapInfo.collection + " / " + envi + "Car"
                );
              } else {
                $("#gbxjs-envi").text(envi + " / " + vehicle);
              }
            }
          } else {
            $("#gbxjs-envi").text(e.mapInfo.collection);
            $("#gbxjs-envi")
              .parent()
              .attr("original-title", "Environment");
          }

          // game mismatches
          if (e.game == "Trackmania®" && isTMX == false) {
            $("#gbxjs-replayerror b").html(
              'ManiaExchange does not support Trackmania® replays. Use <a href="//trackmania.exchange/">TrackmaniaExchange</a> instead.'
            );
            $("#gbxjs-replayerror").show();
          } else if (e.game == "ManiaPlanet" && e.mapInfo.collection != "Stadium®" && isTMX == true) {
            $("#gbxjs-replayerror b").html(
              'TrackmaniaExchange does not support ManiaPlanet replays. Use <a href="//tm.mania.exchange/">ManiaExchange</a> instead.'
            );
            $("#gbxjs-replayerror").show();
          } else if (e.game != "ManiaPlanet" && e.game != "Trackmania®") {
            $("#gbxjs-replayerror b").text(
              (isTMX ? "TrackmaniaExchange" : "ManiaExchange") +
                " does not support " +
                e.game +
                " replays."
            );
            $("#gbxjs-replayerror").show();
          } else if (e.time < 0) {
            $("#gbxjs-replayerror b").text(
              deformatString(buffer.name) + " is not a valid replay."
            );
            $("#gbxjs-replayerror").show();
          }
        } else {
          $("#gbxjs-replayerror b").text(
            deformatString(buffer.name) +
              " is corrupted or belongs to an outdated game."
          );
          $("#gbxjs-replayerror").show();
          $("#gbxjs-information").hide();
        }

        $("#replay-submit button").removeClass("disabled");
        $("#replay-submit, #replay-list").show();
        $("#replay-btn-group .replay-remove").remove();
        $(".replay-info").css("opacity", "1");
      }
    });
  }
}

/**
 * Initalise Multi-Replay Upload (ThaumicTom)
 *
 * @param  {boolean} tmxStatus       - is TMX site?
 * @return {void}
 */
function replayUploadMultiInit(tmxStatus) {
  // Constants
  var isTMX = tmxStatus;
  // Parser
  var form = $(".file-anim");
  var manual = $("#upload_replayfile");
  var files = [],
    dropFile = (isScreen = isMap = multi = false);

  var count = 0;

  manual.on("change", function(e) {
    $(".replay-row").remove();

    files = e.target.files;
    count = 0;
    for (var i = 0; i < files.length; i++) {
      if (files[i].name.match(/\.Replay\.Gbx$/gi) !== null) {
        $("#replay_files").replaceWith(
          manual.clone().attr({
            id: "replay_files",
            name: "replay_files",
            accept: ".gbx",
            style: ""
          })
        );
        processFile(files[i]);
      }
    }
    $("#upload_replayfile").removeAttr("value");
  });

  form
    .on("drag dragstart dragend dragover dragenter dragleave drop", function(
      e
    ) {
      e.preventDefault();
      e.stopPropagation();
    })
    .on("dragover dragenter", function() {
      $(".file-anim").addClass("dragover");
    })
    .on("dragleave dragend drop", function() {
      $(".file-anim").removeClass("dragover");
    })
    .on("drop", function(e) {
      document.querySelector("#replay_files").value = "";
      $(".replay-row").remove();
      $("#replay-submit, #replay-list").hide();

      dropFile = e.originalEvent.dataTransfer.files;

      document.querySelector("#replay_files").files = dropFile;
      count = 0;
      for (var i = 0; i < dropFile.length; i++) {
        $("#gbxjs-replayerror b").text("");
        $("#gbxjs-replayerror").hide();

        processFile(dropFile[i]);
      }
    });

  function processFile(buffer) {
    // gbx.js library by ThaumicTom & BigBang1112 (https://github.com/ThaumicTom/gbx.js)
    new GBX({
      data: buffer,
      onParse: function(e) {
        if (Object.keys(e).length > 0) {
          // clean up
          $("#gbxjs-information").show();
          $("#gbxjs-replayerror, #gbxjs-replaywarn").hide();
          $(".tipsy").remove();
          var mapName = deformatString(e.xml.match(/name="(.*?)"/i)[1]);
          var replayTime = msFormatter(e.time.toString());
          var replayDriver, replayEnvi, replayError;

          // consider name
          if (e.game == "Trackmania®") {
            replayDriver = e.driverNickname;
          } else if (e.game == "Trackmania Turbo") {
            replayDriver = deformatString(e.driverNickname);
          } else {
            replayDriver = e.authorLogin;
          }

          // enivronment
          var envi = e.mapInfo.collection;
          // envi overrides
          if (e.titleUID == "TM2U_Island@adamkooo") {
            envi = "Island";
          } else if (e.titleUID == "TMOneAlpine@unbitn") {
            envi = "Alpine";
          } else if (e.titleUID == "TMOneSpeed@florenzius") {
            envi = "Speed";
          }

          // car
          var vehicle = e.xml.match(/<playermodel id="(.*?)"\/>/i);
          if (vehicle !== null) {
            vehicle = vehicle[1].replace(/\.Item\.Gbx$/gi, "");
            if (e.game == "Trackmania®") {
              if (vehicle == "") {
                replayEnvi = envi + " / " + "CarSport";
              } else {
                replayEnvi = envi + " / " + vehicle;
              }
            } else {
              if (vehicle == "") {
                replayEnvi = e.mapInfo.collection + " / " + envi + "Car";
              } else {
                replayEnvi = envi + " / " + vehicle;
              }
            }
          } else {
            replayEnvi = e.mapInfo.collection;
            //$('#gbxjs-envi').parent().attr('original-title', 'Environment')
          }

          // game mismatches
          if (e.game == "Trackmania®" && isTMX == false) {
            replayError =
              'ManiaExchange does not support Trackmania® replays. Use <a href="//trackmania.exchange/">TrackmaniaExchange</a> instead.';
          } else if (e.game == "ManiaPlanet" && isTMX == true) {
            replayError =
              'TrackmaniaExchange does not support ManiaPlanet replays. Use <a href="//tm.mania.exchange/">ManiaExchange</a> instead.';
          } else if (e.game != "ManiaPlanet" && e.game != "Trackmania®") {
            replayError =
              (isTMX ? "TrackmaniaExchange" : "ManiaExchange") +
              " does not support " +
              e.game +
              " replays.";
          } else if (e.time < 0) {
            replayError =
              deformatString(buffer.name) + " is not a valid replay.";
          }

          var displayFile =
            '<span class="tipsy-n-html" original-title="File name"><i class="far fa-file"></i>&nbsp;' +
            deformatString(buffer.name) +
            "</span>";
          var displayMap =
            '<span class="tipsy-n-html" original-title="Map name"><i class="fas fa-route"></i>&nbsp;' +
            mapName +
            "</span>";
          var displayEnvi =
            '<span class="tipsy-n-html" original-title="Environment / Vehicle"><i class="fas fa-leaf"></i>&nbsp;' +
            replayEnvi +
            "</span>";
          var displayDriver =
            '<span class="tipsy-n-html" original-title="Replay driver"><i class="fas fa-flag-checkered"></i>&nbsp;' +
            replayDriver +
            "</span>";
          var displayTime =
            '<span class="tipsy-n-html" original-title="Replay time"><i class="fas fa-stopwatch"></i>&nbsp;' +
            replayTime +
            "</span>";
          var seperator = "&nbsp;<span>|</span>&nbsp;";
          var displayError =
            '<span><b class="red-text">' + replayError + "</b><br /></span>";
          var displayInformation =
            '<span class="cell-ellipsis" style="display: inline-block; width: 50%;">' +
            displayFile +
            '</span><span class="pull-right" style="display: inline-block; width: 50%;">' +
            displayMap +
            seperator +
            displayEnvi +
            seperator +
            displayDriver +
            seperator +
            displayTime +
            "</span>";
          var displayTemplate =
            '<div class="replay-row"><div class="windowv2-panelbox panelbox-content replay-preview' +
            (count % 2 != 0 ? " alternate" : "") +
            '"><div class="replay-info transition-quick" style="opacity: 0; width: 100%;">' +
            (typeof replayError != "undefined" ? displayError : "") +
            "<span>" +
            displayInformation +
            "</span></div></div></div>";
          $("#replay-list").append(displayTemplate);
        } else {
          var replayError =
            deformatString(buffer.name) +
            " is corrupted or belongs to an outdated game.";
          var displayError =
            '<span><b class="red-text">' + replayError + "</b><br /></span>";
          var displayTemplate =
            '<div class="replay-row"><div class="windowv2-panelbox panelbox-content replay-preview' +
            (count % 2 != 0 ? " alternate" : "") +
            '"><div class="replay-info transition-quick" style="opacity: 0;">' +
            displayError +
            "</div></div></div>";
          $("#replay-list").append(displayTemplate);
        }

        $("#replay-submit button").removeClass("disabled");
        $("#replay-submit, #replay-list").show();
        $("#replay-btn-group .replay-remove").remove();
        $(".replay-info").css("opacity", "1");
        count++; // To alternate background colors
        $("#file-amount").text(count + " replay" + (count != 1 ? "s" : ""));
      }
    });
  }
}

const favbutton = document.querySelector('.tmmapfavouritebutton');
if (favbutton) {
    favbutton.addEventListener('click', addUcFavourite);
}

async function addUcFavourite(event) {
    const elem = event.currentTarget;
    const action = elem.dataset.action;
    const uid = elem.dataset.mapuid;

    if (elem.classList.contains('disabled')) return;

    elem.classList.add('disabled');

    var url = '/api/maps/setingamefavourite';
    url += '?' + new URLSearchParams({ action: action, uid: uid }).toString();

    const response = await fetch(url, { method: 'POST' });
    const result = await response.json();

    if (result.Result === 'OK') {
        successUIforFavourites(elem, action);
    }
    else if (result.Result === '404') {
        alert('Map can not be found on the Trackmania servers.')
    }
    else if (result.Result === 'AUTH') {
        // centering by https://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
        const y = window.top.outerHeight / 2 + window.top.screenY - (800 / 2);
        const x = window.top.outerWidth / 2 + window.top.screenX - (600 / 2);

        overlay.style.opacity = 0.5;
        overlay.style.pointerEvents = '';
        const popup = window.open(`https://account.mania.exchange/account/syncuplayfavorites?initializertoken=${encodeURIComponent(result.Token)}`, 'UPlayFavoritesLink', `width=600,height=800,menubar=no,toolbar=no,top=${y},left=${x}`);

        let openCheckTimer;
        openCheckTimer = window.setInterval(async function () {
            if (popup.closed) {
                window.clearInterval(openCheckTimer);
                overlay.style.opacity = 0;
                overlay.style.pointerEvents = 'none';

                // retry the setfavorite
                const response = await fetch(url, { method: 'POST' });
                const result = await response.json();
                if (result.Result === 'AUTH') {
                    alert('Could not authenticate, try again.');
                }
                else if (result.Result === 'OK') {
                    successUIforFavourites(elem, action);
                }
                else if (result.Result === '404') {
                    alert('Map can not be found on the Trackmania servers.');
                }
                else if (result.Result !== 'OK') {
                    alert('Failed.');
                }

                elem.classList.remove('disabled');
            }
        }, 100);
        return;
    } else {
        alert('Failed.');
    }

    elem.classList.remove('disabled');
}

function successUIforFavourites(elem, action) {
    if (action == 'add') {
        elem.dataset.action = 'remove';
        elem.classList.add('btn-remove');
        elem.setAttribute('original-title', 'Remove map from your ingame favourites');
        elem.firstChild.classList.remove('fa-folder-plus');
        elem.firstChild.classList.add('fa-folder-minus');
    }
    else if (action == 'remove') {
        elem.dataset.action = 'add';
        elem.classList.remove('btn-remove');
        elem.setAttribute('original-title', 'Add map to your ingame favourites');
        elem.firstChild.classList.remove('fa-folder-minus');
        elem.firstChild.classList.add('fa-folder-plus');
    }
}

const relativeTimeFormatter = new Intl.RelativeTimeFormat('en');
const absoluteYearFormatter = new Intl.DateTimeFormat('default', { year: 'numeric' });
const absoluteDateFormatter = new Intl.DateTimeFormat('en-GB', { year: 'numeric', month: 'short', day: 'numeric' });
const absoluteDateNoYearFormatter = new Intl.DateTimeFormat('en-GB', { month: 'short', day: 'numeric' });
const absoluteTimeFormatter = new Intl.DateTimeFormat('default', { hour: 'numeric', minute: 'numeric' });

function formatDate(datetimeStr) {
    const datetime = new Date(datetimeStr);
    return dateString = absoluteDateFormatter.format(datetime);
}

function formatTime(datetimeStr, forceFull) {
    const datetime = new Date(datetimeStr); // add Z for UTC time
    const relativeMs = new Date(Date.now()) - datetime;
    const relativeMin = Math.trunc(relativeMs / (1000 * 60));
    const relativeHr = Math.trunc(relativeMin / 60);

    if (relativeHr >= 24 || forceFull) {
        // use full format
        let dateString = absoluteDateFormatter.format(datetime);
        if (!forceFull) {
            let yesterday = new Date(); // why is this so convoluted in js...
            yesterday.setDate(new Date().getDate() - 1);
            if (dateString === absoluteDateFormatter.format(yesterday)) {
                // shorten date if yesterday
                dateString = 'yesterday';
            } else if (absoluteYearFormatter.format(datetime) === absoluteYearFormatter.format(Date.now())) {
                // shorten date if this year
                dateString = absoluteDateNoYearFormatter.format(datetime);
            }
        }
        return `${dateString} at ${absoluteTimeFormatter.format(datetime)}`;
    } else if (Math.abs(relativeHr) > 0) {
        // use hr format
        return relativeTimeFormatter.format(-relativeHr, 'hours');
    } else if (Math.abs(relativeMin) > 0) {
        // use min format
        return relativeTimeFormatter.format(-relativeMin, 'minutes');
    } else {
        return 'now';
    }
};
/**
 * Track Comments
 */
ManiaExchange.namespace({
	trackComments: {
		/**
		* Constructor
		*/
		init: function () { },

		/**
		* Deletes a comment
		*/
		deleteComment: function (click, element) {
			var commentID = $(click).attr('data-comment-id');
			var selector = element + '[data-comment-id=' + commentID + ']';

			// Confirm
			$.jqDialog.confirm('Are you sure you wish to delete this comment?', function () {
				$.ajax({
					type: 'GET',
					dataType: 'text',
					global: false,
					url: '/tracks/comment/delete/' + commentID,
					statusCode: {
						404: function () {
							$.jqDialog.alert('The resource you\'re looking for cannot be found.');
						},

						403: function () {
							$.jqDialog.alert('Access denied.');
						}
					},
					success: function () {
						$.jqDialog.notify('Comment deleted.', 3);
					}
				});

				// Remove
				$(selector).fadeOut('slow', function () {
					$(this).remove();
				});
			}, function () { });
		}
	}
});;
/**
 * Tracks
 */
ManiaExchange.namespace({
	tracks: {
		/**
		* Constructor
		*/
		init: function () { },

		/**
		* Toggles the visibility of a track.
		*/
		toggleVisibility: function (element, trackID) {
			$(element).replaceWith('<img src="http://images.mania-exchange.com/default/ajax-loader3.gif" alt="" align="absmiddle" />');
		}
	}
});;
(function(){var a;a=function(){return function(a){$(a).live("click",function(a){var b;a.preventDefault();a=$(this).attr("data-confirm-message");b=$(this).attr("data-confirm-href");$("<div></div>").html("<p>"+a+"</p>").dialog({autoOpen:!1,height:Config.jqueryUIDialogHeight,width:Config.jqueryUIDialogWidth,modal:!0,title:"Confirm",buttons:{Confirm:function(){window.location=b},Cancel:function(){$(this).dialog("close")}}}).dialog("open")})}}();$(function(){return new a(".modal-confirm")})}).call(this);;
(function(){this.Tracks=function(){function d(){}d.prototype.deleteComment=function(a){var b,c;b=$(a).attr("data-comment-id");c=a+"[data-comment-id="+b+"]";$.jqDialog.confirm("Are you sure?",function(){$(c).fadeOut("slow",function(){$(this).remove()})},function(){})};d.prototype.toggleState=function(a,b,c){$(a).replaceWith('<img src="http://images.mania-exchange.com/default/ajax-loader3.gif" alt="" align="absmiddle" title="Executing action. Please wait ..." />');$.ajax({type:"GET",url:"/api/tracks/toggle/"+
b+"/"+c+"/",success:function(){location.reload(!0)}})};return d}()}).call(this);;
(function(){this.PlayPalOnline=function(){function a(){}a.prototype.removeTrack=function(){};return a}()}).call(this);;
(function(){this.Users=function(){function a(){}a.prototype.favourite=function(a,c,b){$(a).replaceWith('<img src="http://images.mania-exchange.com/default/ajax-loader3.gif" alt="" align="absmiddle" title="Executing action. Please wait ..." />');$.ajax({type:"GET",url:"/api/users/favourite/"+b+"?option="+c,error:function(a,c,b){return alert(a.status+" - "+b)},success:function(){location.reload(!0)}})};return a}()}).call(this);
;
(function(){var a;a=this.ManiaExchange={};a.version="1.0.0";a.tracks=new Tracks;a.ppo=new PlayPalOnline;a.users=new Users;window.ManiaExchange=a}).call(this);;
var MXCore = Class.create({

	/**
	* Constructor
	*/
	init: function () { },

	/**
	* Posts a form
	*/
	//postForm: function (form, anchor) {
	//	var ieForm = form.replace("form#", "");
	//	var ua = $.browser;

	//	if (ua.msie && (ua.version.slice(0, 3) == "8.0") || (ua.version.slice(0, 3) == "7.0")) {
	//		$(anchor).click(function (ev) {
	//			ev.preventDefault();
	//			var ieForm2 = document.forms[ieForm];
	//			ieForm2.submit();
	//		});
	//	}
	//	else {
	//		$(anchor).click(function (ev) {
	//			ev.preventDefault();
	//			$(form).submit();
	//		});
	//	}
	//},

	/**
	* Loads track info boxes
	*/
	//loadTrackInfoBoxes: function () {
	//	var id = "div#track-info-box";

	//	$(id).each(function (i) {
	//		var trackID = $(this).attr("track-id");
	//		var display = $(this).attr("display-style");

	//		// Load the HTML info box
	//		$(this).load("/api/tracks/info_box/html/" + trackID + "/" + display);
	//	});
	//}
});

// Initialize here
var mxc = new MXCore();;
/**
 * Tracks
 */
var MXTracks = Class.create({

	/**
	* Constructor
	*/
	init: function () { },

	/**
	* Toggles the online record selection.
	*/
	toggleOnlineRecords: function (anchor, provider, trackUID) {
		// Load the records from the API into the container.
		this.loadOnlineRecords(provider, trackUID);

		// Highlight selection with bold class.
		$('#' + anchor).addClass("bold");
	},

	/**
	* Loads online records for the specified record provider.
	*/
	loadOnlineRecords: function (provider, trackUID) {
	    var el = $("#online-records-window");

	    setLoading(el, true);

		// Determine the full record provider name from the shorthand/abbreviation.
		var fullProvider = (provider == "dm" ? "dedimania" : "tmio");

		$.ajax({
			type: 'GET',
			dataType: 'html',
			global: false,
			url: '/api/' + fullProvider + '/get_online_records/' + trackUID,
			success: function (data) {
			    el.children().html(data);
			    setLoading(el, false);
			}
		});

	}

});

// Initalize
var mxt = new MXTracks();;
/**
 * Various functions
 */
(function ($) {
	$.fn.scrollLoad = function (options) {

		var defaults = {
			url: '',
			ScrollAfterHeight: 90,
			onload: function (data) {
				alert(data);
			},
			start: function () { },
			continueWhile: function () {
				return true;
			}
		};

		//		for (var eachProperty in defaults) {
		//			if (options[eachProperty]) {
		//				defaults[eachProperty] = options[eachProperty];
		//			}
		//		}

		var options = $.extend(defaults, options);

		return this.each(function () {
			var o = options;
			this.scrolling = false;
			this.scrollPrev = this.onscroll ? this.onscroll : null;
			$(this).bind('scroll', function (e) {
				if (this.scrollPrev) {
					this.scrollPrev();
				}

				if (this.scrolling) {
					return;
				}

				if (Math.round($(this).attr('scrollTop') / ($(this)[0].scrollHeight - $(this).attr('clientHeight')) * 100) > o.ScrollAfterHeight) {
					o.start.call($(this)[0]);
					this.scrolling = true;
					$.ajax({
						url: o.url,
						type: 'GET',
						success: function (data) {
							$(this).scrolling = false;
							o.onload.call($(this)[0], data);
							if (!o.continueWhile.call($(this)[0])) {
								$(this).unbind('scroll');
							}
						}
					});
				}
			});
		});
	};

	/**
	*	postForm - Submits the specified form when the specified link is clicked
	*	fastforza (19 February, 2011 - 8.52pm)
	*/
	//postForm = function () {
	//	var args = arguments[0]; // Get function arguments
	//	var form = args.form;
	//	var a = args.a;
	//	var ieForm = form.replace("form#", "");

	//	$(a).click(function () {
	//		var ieForm2 = document.forms[ieForm];
	//		ieForm2.submit();
	//	});
	//};

	/**
	*	textExecutePreview - POSTs data to the BBcode API and returns formatted HTML in a preview div
	*	fastforza (04 March, 2011 - 8.22PM)
	*/
	textExecutePreview = function () {
		var args = arguments[0];
		var textAreaString = "textarea" + args.textarea;
		//Get a reference to the CodeMirror editor
		var editor = $(textAreaString).next('.CodeMirror')[0].CodeMirror;


		var commentString = editor.getValue();

		if (commentString.length > 0) {
			$("div.editor-preview").show();
		}

		if (commentString.length > 20000) {
			alert("Message is too long to preview.");
        }

        var collection = 0;
        var formattracks = true;
        if (args.collection)
            collection = args.collection;
        if (args.collection == 3)
            formattracks = false;

		$.ajax({
			type: 'POST',
			dataType: 'text',
			global: false,
            url: '/api/bbcode?formattracks=' + formattracks + '&collection=' + collection,
            data: 'data=' + encodeURIComponent(commentString),
			success: function (data) {
				$("div.editor-preview").html(data.replace("/[\r\n]+/g", "<br />"));
			}
		});
	};

	/**
	*	confirmDialog - Shows a jqDialog confirmation window using custom HREFs and attributes
	*	fastforza (13 March, 2011 - 12.01PM)
	*/
	confirmDialog = function () {
		var args = arguments[0];
		var anchor = args.anchor;
		var message;

		// .live() fixes a problem jQuery has when binding events to AJAX loaded HTML
		$(anchor).live('click', function () {
			var href = $(this).attr("real-href");
			message = $(this).attr("confirm-message");

			$.jqDialog.confirm(message, function () {
				window.location = href;
			}, function () { });
		});
	};

	confirmDialog2 = function () {
		var args = arguments[0];
		var anchor = args.anchor;
		var remove = args.remove;
		var successMessage = args.successMessage;
		var message;

		// .live() fixes a problem jQuery has when binding events to AJAX loaded HTML
		$(anchor).live('click', function () {
			var href = $(this).attr("real-href");
			message = $(this).attr("confirm-message");

			$.jqDialog.confirm(message, function () {
				$.ajax({
					url: href,
					success: function () {
						$.jqDialog.notify(successMessage, 3);
					}
				});
			}, function () { });
		});
	};

	/**
	* setActiveTab - Doesn't really set a tab per se; it just adds an active class to the specified anchor.
	* fastforza (12 April, 2011 - 12.42PM)
	*/
	setActiveTab = function () {
		var args = arguments[0];
		var anchor = args.anchor;

		$(anchor).addClass("active");
	};

})(jQuery);

/**
* Performs a javascript page selection via the API
* fastforza (7 July, 2011 - 3:07PM)
*/
function pageSelector(selector, resource, page) {
    var el = $("#" + selector);

    setLoading(el, true);

	var date = new Date();

	$.ajax({
		type: "GET",
		dataType: "html",
		global: false,
		url: resource + "?page=" + page + "&dummy=" + date.getTime(),
		success: function (data) {
		    el.children().html(data);
		    setLoading(el, false);
		}
	});

	return false;
}

function submitForm(index) {
	if (typeof (index) == 'undefined') {
		index = 0;
	}

	document.forms[index].submit();
}

function disableButton() {
	var button = document.getElementById("mx-form-button");
	button.disabled = true;
	return true;
}

function setLoading(jqElement, isLoading) {
    //(isLoading && jqElement.addClass('windowv2-loading')) || jqElement.removeClass('windowv2-loading');
	if (isLoading) {
		jqElement.addClass('windowv2-loading');
		if (isTMX) {
            jqElement.prepend('<img oncontextmenu="return false;" class="spin_p mxloader" src="https://images.mania.exchange/logos/tmx/dot.png" />\
                              <img oncontextmenu="return false;" class="spin_p spin_ccw mxloader" src="https://images.mania.exchange/logos/tmx/x_sm.png" />\
                              <svg class="spinner mxloader" viewBox= "0 0 50 50">\
                              <circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>\
                              </svg>');
		} else {
			jqElement.prepend('<img oncontextmenu="return false;" class="spin_p mxloader" src="https://images.mania.exchange/logos/mx/dot.png" />\
                              <img oncontextmenu="return false;" class="spin_p spin_ccw mxloader" src="https://images.mania.exchange/logos/mx/x_sm.png" />\
                              <svg class="spinner mxloader" viewBox= "0 0 50 50">\
                              <circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>\
                              </svg>');
		}
	}
    else
    {
        jqElement.removeClass('windowv2-loading');
        $(jqElement).children('svg').remove();
        $(jqElement).children('.mxloader').remove();
    }
}

function showMessage(success, message) {
    var elem = $('<div class="result-message result-message-' + (success ? 'success' : 'error') + '"></div>').text(message).prependTo($('body'));
    elem.hide().fadeIn(150);
    setTimeout(function () {
        elem.fadeOut(150, function () {
            elem.remove();
        });
    }, 3000);
}

function userLink(userlink) {
    return $('<a href="/user/profile/' + userlink.UserID + '"></a>').text(userlink.Username);
};
// Bust them bitches out.
if(top!=self){top.location.replace(document.location)};
/**
* Forzyy's jQuery and other sexy stuff
* (c) forzyy - tm-exchange.com
* June 2010
*/

/**
* onKeyPress
* PostBack enter pressed handler
*/
function DoEnterSubmit(e, et) {
	if ((window.event ? window.event.keyCode : e.which) == 13) {
		__doPostBack(et, '');
		return false;
	}
}

/**
* jQuery Menu
* June 26, 2010
*//*
var timeout = 500;
var closetimer = 0;
var ddmenuitem = 0;

function mpxmenu_open() {
	mpxmenu_canceltimer();
	mpxmenu_close();
	ddmenuitem = $(this).find('ul').eq(0).css('visibility', 'visible');
}

function mpxmenu_close() {
	if (ddmenuitem) {
		ddmenuitem.css('visibility', 'hidden');
	}
}

function mpxmenu_timer() {
	closetimer = window.setTimeout(mpxmenu_close, timeout);
}

function mpxmenu_canceltimer() {
	if (closetimer) {
		window.clearTimeout(closetimer);
		closetimer = null;
	} 
}

$(document).ready(function () {
	$('#mpxmenu > li').bind('mouseover', mpxmenu_open);
	$('#mpxmenu > li').bind('mouseout', mpxmenu_timer);
	
	$('#mpxtoolbar > li').bind('mouseover', mpxmenu_open);
	$('#mpxtoolbar > li').bind('mouseout', mpxmenu_timer);
});

   document.onclick = mpxmenu_close;*/

/**
* jQuery Highlight Animation - http://lincolnloop.com/blog/2009/apr/17/highlighting-named-anchors-jquery/
* October, 18, 2010
*/

/**
* JS Forum Jump
* October, 24, 2010
*/
function mpx_forumjump() {
	var newURL = document.aspnetForm.ForumJump;

	if (newURL.options[newURL.selectedIndex].value != "-1") {
		url = newURL.options[newURL.selectedIndex].value
		location.href = url
	}
}

/**
* Disables a button
* December, 7, 2010
*/
function disableButton(buttonID) {
	// Disable it ...
	document.getElementById(buttonID).disabled = "true";

	// Perform a postback on it
	__doPostBack(buttonID, '');
}

/**
* Disables an array of fields when the checkbox is checked
* January, 24th, 2011
*/
function checkbox_disable(checkbox, hideObjects) {

	// Checkbox is checked
	if ($('input[id=' + checkbox + ']').is(':checked')) {

		// Iterate through all objects
		$.each(hideObjects, function (index, value) {

			// Disable the input
			$('input[id=' + value + ']').attr('disabled', true);

		});
	}

	// Not checked
	else {

		// Iterate through all objects
		$.each(hideObjects, function (index, value) {

			// Enable the input
			$('input[id=' + value + ']').attr('disabled', false);

		});
	}
}

/**
* Disables an array of inputs when a radio is selected
* January, 25th, 2011
*/
function radio_disableinputs(radio, inputObjects) {

	$(radio).click(function () {

		$.each(inputObjects, function (index, value) {
			$(value).attr("disabled", "disabled");
		});

	});

}

/**
* Posts the specified form
* February, 18th, 2011
*/
function mvc_formpost(form) {

	$(form).submit(function (e) {
		$.post(this).attr("action");
		e.preventDefault();
	});

};
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

// This is CodeMirror (https://codemirror.net), a code editor
// implemented in JavaScript on top of the browser's DOM.
//
// You can find some technical background for some of the code below
// at http://marijnhaverbeke.nl/blog/#cm-internals .

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global.CodeMirror = factory());
}(this, (function () { 'use strict';

  // Kludges for bugs and behavior differences that can't be feature
  // detected are enabled based on userAgent etc sniffing.
  var userAgent = navigator.userAgent;
  var platform = navigator.platform;

  var gecko = /gecko\/\d/i.test(userAgent);
  var ie_upto10 = /MSIE \d/.test(userAgent);
  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
  var edge = /Edge\/(\d+)/.exec(userAgent);
  var ie = ie_upto10 || ie_11up || edge;
  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
  var webkit = !edge && /WebKit\//.test(userAgent);
  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
  var chrome = !edge && /Chrome\//.test(userAgent);
  var presto = /Opera\//.test(userAgent);
  var safari = /Apple Computer/.test(navigator.vendor);
  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
  var phantom = /PhantomJS/.test(userAgent);

  var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
  var android = /Android/.test(userAgent);
  // This is woefully incomplete. Suggestions for alternative methods welcome.
  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
  var mac = ios || /Mac/.test(platform);
  var chromeOS = /\bCrOS\b/.test(userAgent);
  var windows = /win/i.test(platform);

  var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
  if (presto_version) { presto_version = Number(presto_version[1]); }
  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
  var captureRightClick = gecko || (ie && ie_version >= 9);

  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }

  var rmClass = function(node, cls) {
    var current = node.className;
    var match = classTest(cls).exec(current);
    if (match) {
      var after = current.slice(match.index + match[0].length);
      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
    }
  };

  function removeChildren(e) {
    for (var count = e.childNodes.length; count > 0; --count)
      { e.removeChild(e.firstChild); }
    return e
  }

  function removeChildrenAndAdd(parent, e) {
    return removeChildren(parent).appendChild(e)
  }

  function elt(tag, content, className, style) {
    var e = document.createElement(tag);
    if (className) { e.className = className; }
    if (style) { e.style.cssText = style; }
    if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
    return e
  }
  // wrapper for elt, which removes the elt from the accessibility tree
  function eltP(tag, content, className, style) {
    var e = elt(tag, content, className, style);
    e.setAttribute("role", "presentation");
    return e
  }

  var range;
  if (document.createRange) { range = function(node, start, end, endNode) {
    var r = document.createRange();
    r.setEnd(endNode || node, end);
    r.setStart(node, start);
    return r
  }; }
  else { range = function(node, start, end) {
    var r = document.body.createTextRange();
    try { r.moveToElementText(node.parentNode); }
    catch(e) { return r }
    r.collapse(true);
    r.moveEnd("character", end);
    r.moveStart("character", start);
    return r
  }; }

  function contains(parent, child) {
    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
      { child = child.parentNode; }
    if (parent.contains)
      { return parent.contains(child) }
    do {
      if (child.nodeType == 11) { child = child.host; }
      if (child == parent) { return true }
    } while (child = child.parentNode)
  }

  function activeElt() {
    // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
    // IE < 10 will throw when accessed while the page is loading or in an iframe.
    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
    var activeElement;
    try {
      activeElement = document.activeElement;
    } catch(e) {
      activeElement = document.body || null;
    }
    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
      { activeElement = activeElement.shadowRoot.activeElement; }
    return activeElement
  }

  function addClass(node, cls) {
    var current = node.className;
    if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
  }
  function joinClasses(a, b) {
    var as = a.split(" ");
    for (var i = 0; i < as.length; i++)
      { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
    return b
  }

  var selectInput = function(node) { node.select(); };
  if (ios) // Mobile Safari apparently has a bug where select() is broken.
    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
  else if (ie) // Suppress mysterious IE10 errors
    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }

  function bind(f) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function(){return f.apply(null, args)}
  }

  function copyObj(obj, target, overwrite) {
    if (!target) { target = {}; }
    for (var prop in obj)
      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
        { target[prop] = obj[prop]; } }
    return target
  }

  // Counts the column offset in a string, taking tabs into account.
  // Used mostly to find indentation.
  function countColumn(string, end, tabSize, startIndex, startValue) {
    if (end == null) {
      end = string.search(/[^\s\u00a0]/);
      if (end == -1) { end = string.length; }
    }
    for (var i = startIndex || 0, n = startValue || 0;;) {
      var nextTab = string.indexOf("\t", i);
      if (nextTab < 0 || nextTab >= end)
        { return n + (end - i) }
      n += nextTab - i;
      n += tabSize - (n % tabSize);
      i = nextTab + 1;
    }
  }

  var Delayed = function() {
    this.id = null;
    this.f = null;
    this.time = 0;
    this.handler = bind(this.onTimeout, this);
  };
  Delayed.prototype.onTimeout = function (self) {
    self.id = 0;
    if (self.time <= +new Date) {
      self.f();
    } else {
      setTimeout(self.handler, self.time - +new Date);
    }
  };
  Delayed.prototype.set = function (ms, f) {
    this.f = f;
    var time = +new Date + ms;
    if (!this.id || time < this.time) {
      clearTimeout(this.id);
      this.id = setTimeout(this.handler, ms);
      this.time = time;
    }
  };

  function indexOf(array, elt) {
    for (var i = 0; i < array.length; ++i)
      { if (array[i] == elt) { return i } }
    return -1
  }

  // Number of pixels added to scroller and sizer to hide scrollbar
  var scrollerGap = 30;

  // Returned or thrown by various protocols to signal 'I'm not
  // handling this'.
  var Pass = {toString: function(){return "CodeMirror.Pass"}};

  // Reused option objects for setSelection & friends
  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};

  // The inverse of countColumn -- find the offset that corresponds to
  // a particular column.
  function findColumn(string, goal, tabSize) {
    for (var pos = 0, col = 0;;) {
      var nextTab = string.indexOf("\t", pos);
      if (nextTab == -1) { nextTab = string.length; }
      var skipped = nextTab - pos;
      if (nextTab == string.length || col + skipped >= goal)
        { return pos + Math.min(skipped, goal - col) }
      col += nextTab - pos;
      col += tabSize - (col % tabSize);
      pos = nextTab + 1;
      if (col >= goal) { return pos }
    }
  }

  var spaceStrs = [""];
  function spaceStr(n) {
    while (spaceStrs.length <= n)
      { spaceStrs.push(lst(spaceStrs) + " "); }
    return spaceStrs[n]
  }

  function lst(arr) { return arr[arr.length-1] }

  function map(array, f) {
    var out = [];
    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
    return out
  }

  function insertSorted(array, value, score) {
    var pos = 0, priority = score(value);
    while (pos < array.length && score(array[pos]) <= priority) { pos++; }
    array.splice(pos, 0, value);
  }

  function nothing() {}

  function createObj(base, props) {
    var inst;
    if (Object.create) {
      inst = Object.create(base);
    } else {
      nothing.prototype = base;
      inst = new nothing();
    }
    if (props) { copyObj(props, inst); }
    return inst
  }

  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
  function isWordCharBasic(ch) {
    return /\w/.test(ch) || ch > "\x80" &&
      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
  }
  function isWordChar(ch, helper) {
    if (!helper) { return isWordCharBasic(ch) }
    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
    return helper.test(ch)
  }

  function isEmpty(obj) {
    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
    return true
  }

  // Extending unicode characters. A series of a non-extending char +
  // any number of extending chars is treated as a single unit as far
  // as editing and measuring is concerned. This is not fully correct,
  // since some scripts/fonts/browsers also treat other configurations
  // of code points as a group.
  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }

  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
  function skipExtendingChars(str, pos, dir) {
    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
    return pos
  }

  // Returns the value from the range [`from`; `to`] that satisfies
  // `pred` and is closest to `from`. Assumes that at least `to`
  // satisfies `pred`. Supports `from` being greater than `to`.
  function findFirst(pred, from, to) {
    // At any point we are certain `to` satisfies `pred`, don't know
    // whether `from` does.
    var dir = from > to ? -1 : 1;
    for (;;) {
      if (from == to) { return from }
      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
      if (mid == from) { return pred(mid) ? from : to }
      if (pred(mid)) { to = mid; }
      else { from = mid + dir; }
    }
  }

  // BIDI HELPERS

  function iterateBidiSections(order, from, to, f) {
    if (!order) { return f(from, to, "ltr", 0) }
    var found = false;
    for (var i = 0; i < order.length; ++i) {
      var part = order[i];
      if (part.from < to && part.to > from || from == to && part.to == from) {
        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
        found = true;
      }
    }
    if (!found) { f(from, to, "ltr"); }
  }

  var bidiOther = null;
  function getBidiPartAt(order, ch, sticky) {
    var found;
    bidiOther = null;
    for (var i = 0; i < order.length; ++i) {
      var cur = order[i];
      if (cur.from < ch && cur.to > ch) { return i }
      if (cur.to == ch) {
        if (cur.from != cur.to && sticky == "before") { found = i; }
        else { bidiOther = i; }
      }
      if (cur.from == ch) {
        if (cur.from != cur.to && sticky != "before") { found = i; }
        else { bidiOther = i; }
      }
    }
    return found != null ? found : bidiOther
  }

  // Bidirectional ordering algorithm
  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
  // that this (partially) implements.

  // One-char codes used for character types:
  // L (L):   Left-to-Right
  // R (R):   Right-to-Left
  // r (AL):  Right-to-Left Arabic
  // 1 (EN):  European Number
  // + (ES):  European Number Separator
  // % (ET):  European Number Terminator
  // n (AN):  Arabic Number
  // , (CS):  Common Number Separator
  // m (NSM): Non-Spacing Mark
  // b (BN):  Boundary Neutral
  // s (B):   Paragraph Separator
  // t (S):   Segment Separator
  // w (WS):  Whitespace
  // N (ON):  Other Neutrals

  // Returns null if characters are ordered as they appear
  // (left-to-right), or an array of sections ({from, to, level}
  // objects) in the order in which they occur visually.
  var bidiOrdering = (function() {
    // Character types for codepoints 0 to 0xff
    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
    // Character types for codepoints 0x600 to 0x6f9
    var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
    function charType(code) {
      if (code <= 0xf7) { return lowTypes.charAt(code) }
      else if (0x590 <= code && code <= 0x5f4) { return "R" }
      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
      else if (0x6ee <= code && code <= 0x8ac) { return "r" }
      else if (0x2000 <= code && code <= 0x200b) { return "w" }
      else if (code == 0x200c) { return "b" }
      else { return "L" }
    }

    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;

    function BidiSpan(level, from, to) {
      this.level = level;
      this.from = from; this.to = to;
    }

    return function(str, direction) {
      var outerType = direction == "ltr" ? "L" : "R";

      if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
      var len = str.length, types = [];
      for (var i = 0; i < len; ++i)
        { types.push(charType(str.charCodeAt(i))); }

      // W1. Examine each non-spacing mark (NSM) in the level run, and
      // change the type of the NSM to the type of the previous
      // character. If the NSM is at the start of the level run, it will
      // get the type of sor.
      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
        var type = types[i$1];
        if (type == "m") { types[i$1] = prev; }
        else { prev = type; }
      }

      // W2. Search backwards from each instance of a European number
      // until the first strong type (R, L, AL, or sor) is found. If an
      // AL is found, change the type of the European number to Arabic
      // number.
      // W3. Change all ALs to R.
      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
        var type$1 = types[i$2];
        if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
      }

      // W4. A single European separator between two European numbers
      // changes to a European number. A single common separator between
      // two numbers of the same type changes to that type.
      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
        var type$2 = types[i$3];
        if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
        else if (type$2 == "," && prev$1 == types[i$3+1] &&
                 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
        prev$1 = type$2;
      }

      // W5. A sequence of European terminators adjacent to European
      // numbers changes to all European numbers.
      // W6. Otherwise, separators and terminators change to Other
      // Neutral.
      for (var i$4 = 0; i$4 < len; ++i$4) {
        var type$3 = types[i$4];
        if (type$3 == ",") { types[i$4] = "N"; }
        else if (type$3 == "%") {
          var end = (void 0);
          for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
          var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
          for (var j = i$4; j < end; ++j) { types[j] = replace; }
          i$4 = end - 1;
        }
      }

      // W7. Search backwards from each instance of a European number
      // until the first strong type (R, L, or sor) is found. If an L is
      // found, then change the type of the European number to L.
      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
        var type$4 = types[i$5];
        if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
        else if (isStrong.test(type$4)) { cur$1 = type$4; }
      }

      // N1. A sequence of neutrals takes the direction of the
      // surrounding strong text if the text on both sides has the same
      // direction. European and Arabic numbers act as if they were R in
      // terms of their influence on neutrals. Start-of-level-run (sor)
      // and end-of-level-run (eor) are used at level run boundaries.
      // N2. Any remaining neutrals take the embedding direction.
      for (var i$6 = 0; i$6 < len; ++i$6) {
        if (isNeutral.test(types[i$6])) {
          var end$1 = (void 0);
          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
          var before = (i$6 ? types[i$6-1] : outerType) == "L";
          var after = (end$1 < len ? types[end$1] : outerType) == "L";
          var replace$1 = before == after ? (before ? "L" : "R") : outerType;
          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
          i$6 = end$1 - 1;
        }
      }

      // Here we depart from the documented algorithm, in order to avoid
      // building up an actual levels array. Since there are only three
      // levels (0, 1, 2) in an implementation that doesn't take
      // explicit embedding into account, we can build up the order on
      // the fly, without following the level-based algorithm.
      var order = [], m;
      for (var i$7 = 0; i$7 < len;) {
        if (countsAsLeft.test(types[i$7])) {
          var start = i$7;
          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
          order.push(new BidiSpan(0, start, i$7));
        } else {
          var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
          for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
          for (var j$2 = pos; j$2 < i$7;) {
            if (countsAsNum.test(types[j$2])) {
              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
              var nstart = j$2;
              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
              order.splice(at, 0, new BidiSpan(2, nstart, j$2));
              at += isRTL;
              pos = j$2;
            } else { ++j$2; }
          }
          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
        }
      }
      if (direction == "ltr") {
        if (order[0].level == 1 && (m = str.match(/^\s+/))) {
          order[0].from = m[0].length;
          order.unshift(new BidiSpan(0, 0, m[0].length));
        }
        if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
          lst(order).to -= m[0].length;
          order.push(new BidiSpan(0, len - m[0].length, len));
        }
      }

      return direction == "rtl" ? order.reverse() : order
    }
  })();

  // Get the bidi ordering for the given line (and cache it). Returns
  // false for lines that are fully left-to-right, and an array of
  // BidiSpan objects otherwise.
  function getOrder(line, direction) {
    var order = line.order;
    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
    return order
  }

  // EVENT HANDLING

  // Lightweight event framework. on/off also work on DOM nodes,
  // registering native DOM handlers.

  var noHandlers = [];

  var on = function(emitter, type, f) {
    if (emitter.addEventListener) {
      emitter.addEventListener(type, f, false);
    } else if (emitter.attachEvent) {
      emitter.attachEvent("on" + type, f);
    } else {
      var map$$1 = emitter._handlers || (emitter._handlers = {});
      map$$1[type] = (map$$1[type] || noHandlers).concat(f);
    }
  };

  function getHandlers(emitter, type) {
    return emitter._handlers && emitter._handlers[type] || noHandlers
  }

  function off(emitter, type, f) {
    if (emitter.removeEventListener) {
      emitter.removeEventListener(type, f, false);
    } else if (emitter.detachEvent) {
      emitter.detachEvent("on" + type, f);
    } else {
      var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
      if (arr) {
        var index = indexOf(arr, f);
        if (index > -1)
          { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
      }
    }
  }

  function signal(emitter, type /*, values...*/) {
    var handlers = getHandlers(emitter, type);
    if (!handlers.length) { return }
    var args = Array.prototype.slice.call(arguments, 2);
    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
  }

  // The DOM events that CodeMirror handles can be overridden by
  // registering a (non-DOM) handler on the editor for the event name,
  // and preventDefault-ing the event in that handler.
  function signalDOMEvent(cm, e, override) {
    if (typeof e == "string")
      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
    signal(cm, override || e.type, cm, e);
    return e_defaultPrevented(e) || e.codemirrorIgnore
  }

  function signalCursorActivity(cm) {
    var arr = cm._handlers && cm._handlers.cursorActivity;
    if (!arr) { return }
    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
      { set.push(arr[i]); } }
  }

  function hasHandler(emitter, type) {
    return getHandlers(emitter, type).length > 0
  }

  // Add on and off methods to a constructor's prototype, to make
  // registering events on such objects more convenient.
  function eventMixin(ctor) {
    ctor.prototype.on = function(type, f) {on(this, type, f);};
    ctor.prototype.off = function(type, f) {off(this, type, f);};
  }

  // Due to the fact that we still support jurassic IE versions, some
  // compatibility wrappers are needed.

  function e_preventDefault(e) {
    if (e.preventDefault) { e.preventDefault(); }
    else { e.returnValue = false; }
  }
  function e_stopPropagation(e) {
    if (e.stopPropagation) { e.stopPropagation(); }
    else { e.cancelBubble = true; }
  }
  function e_defaultPrevented(e) {
    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
  }
  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}

  function e_target(e) {return e.target || e.srcElement}
  function e_button(e) {
    var b = e.which;
    if (b == null) {
      if (e.button & 1) { b = 1; }
      else if (e.button & 2) { b = 3; }
      else if (e.button & 4) { b = 2; }
    }
    if (mac && e.ctrlKey && b == 1) { b = 3; }
    return b
  }

  // Detect drag-and-drop
  var dragAndDrop = function() {
    // There is *some* kind of drag-and-drop support in IE6-8, but I
    // couldn't get it to work yet.
    if (ie && ie_version < 9) { return false }
    var div = elt('div');
    return "draggable" in div || "dragDrop" in div
  }();

  var zwspSupported;
  function zeroWidthElement(measure) {
    if (zwspSupported == null) {
      var test = elt("span", "\u200b");
      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
      if (measure.firstChild.offsetHeight != 0)
        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
    }
    var node = zwspSupported ? elt("span", "\u200b") :
      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
    node.setAttribute("cm-text", "");
    return node
  }

  // Feature-detect IE's crummy client rect reporting for bidi text
  var badBidiRects;
  function hasBadBidiRects(measure) {
    if (badBidiRects != null) { return badBidiRects }
    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
    var r0 = range(txt, 0, 1).getBoundingClientRect();
    var r1 = range(txt, 1, 2).getBoundingClientRect();
    removeChildren(measure);
    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
    return badBidiRects = (r1.right - r0.right < 3)
  }

  // See if "".split is the broken IE version, if so, provide an
  // alternative way to split lines.
  var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
    var pos = 0, result = [], l = string.length;
    while (pos <= l) {
      var nl = string.indexOf("\n", pos);
      if (nl == -1) { nl = string.length; }
      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
      var rt = line.indexOf("\r");
      if (rt != -1) {
        result.push(line.slice(0, rt));
        pos += rt + 1;
      } else {
        result.push(line);
        pos = nl + 1;
      }
    }
    return result
  } : function (string) { return string.split(/\r\n?|\n/); };

  var hasSelection = window.getSelection ? function (te) {
    try { return te.selectionStart != te.selectionEnd }
    catch(e) { return false }
  } : function (te) {
    var range$$1;
    try {range$$1 = te.ownerDocument.selection.createRange();}
    catch(e) {}
    if (!range$$1 || range$$1.parentElement() != te) { return false }
    return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
  };

  var hasCopyEvent = (function () {
    var e = elt("div");
    if ("oncopy" in e) { return true }
    e.setAttribute("oncopy", "return;");
    return typeof e.oncopy == "function"
  })();

  var badZoomedRects = null;
  function hasBadZoomedRects(measure) {
    if (badZoomedRects != null) { return badZoomedRects }
    var node = removeChildrenAndAdd(measure, elt("span", "x"));
    var normal = node.getBoundingClientRect();
    var fromRange = range(node, 0, 1).getBoundingClientRect();
    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
  }

  // Known modes, by name and by MIME
  var modes = {}, mimeModes = {};

  // Extra arguments are stored as the mode's dependencies, which is
  // used by (legacy) mechanisms like loadmode.js to automatically
  // load a mode. (Preferred mechanism is the require/define calls.)
  function defineMode(name, mode) {
    if (arguments.length > 2)
      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
    modes[name] = mode;
  }

  function defineMIME(mime, spec) {
    mimeModes[mime] = spec;
  }

  // Given a MIME type, a {name, ...options} config object, or a name
  // string, return a mode config object.
  function resolveMode(spec) {
    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
      spec = mimeModes[spec];
    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
      var found = mimeModes[spec.name];
      if (typeof found == "string") { found = {name: found}; }
      spec = createObj(found, spec);
      spec.name = found.name;
    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
      return resolveMode("application/xml")
    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
      return resolveMode("application/json")
    }
    if (typeof spec == "string") { return {name: spec} }
    else { return spec || {name: "null"} }
  }

  // Given a mode spec (anything that resolveMode accepts), find and
  // initialize an actual mode object.
  function getMode(options, spec) {
    spec = resolveMode(spec);
    var mfactory = modes[spec.name];
    if (!mfactory) { return getMode(options, "text/plain") }
    var modeObj = mfactory(options, spec);
    if (modeExtensions.hasOwnProperty(spec.name)) {
      var exts = modeExtensions[spec.name];
      for (var prop in exts) {
        if (!exts.hasOwnProperty(prop)) { continue }
        if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
        modeObj[prop] = exts[prop];
      }
    }
    modeObj.name = spec.name;
    if (spec.helperType) { modeObj.helperType = spec.helperType; }
    if (spec.modeProps) { for (var prop$1 in spec.modeProps)
      { modeObj[prop$1] = spec.modeProps[prop$1]; } }

    return modeObj
  }

  // This can be used to attach properties to mode objects from
  // outside the actual mode definition.
  var modeExtensions = {};
  function extendMode(mode, properties) {
    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
    copyObj(properties, exts);
  }

  function copyState(mode, state) {
    if (state === true) { return state }
    if (mode.copyState) { return mode.copyState(state) }
    var nstate = {};
    for (var n in state) {
      var val = state[n];
      if (val instanceof Array) { val = val.concat([]); }
      nstate[n] = val;
    }
    return nstate
  }

  // Given a mode and a state (for that mode), find the inner mode and
  // state at the position that the state refers to.
  function innerMode(mode, state) {
    var info;
    while (mode.innerMode) {
      info = mode.innerMode(state);
      if (!info || info.mode == mode) { break }
      state = info.state;
      mode = info.mode;
    }
    return info || {mode: mode, state: state}
  }

  function startState(mode, a1, a2) {
    return mode.startState ? mode.startState(a1, a2) : true
  }

  // STRING STREAM

  // Fed to the mode parsers, provides helper functions to make
  // parsers more succinct.

  var StringStream = function(string, tabSize, lineOracle) {
    this.pos = this.start = 0;
    this.string = string;
    this.tabSize = tabSize || 8;
    this.lastColumnPos = this.lastColumnValue = 0;
    this.lineStart = 0;
    this.lineOracle = lineOracle;
  };

  StringStream.prototype.eol = function () {return this.pos >= this.string.length};
  StringStream.prototype.sol = function () {return this.pos == this.lineStart};
  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
  StringStream.prototype.next = function () {
    if (this.pos < this.string.length)
      { return this.string.charAt(this.pos++) }
  };
  StringStream.prototype.eat = function (match) {
    var ch = this.string.charAt(this.pos);
    var ok;
    if (typeof match == "string") { ok = ch == match; }
    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
    if (ok) {++this.pos; return ch}
  };
  StringStream.prototype.eatWhile = function (match) {
    var start = this.pos;
    while (this.eat(match)){}
    return this.pos > start
  };
  StringStream.prototype.eatSpace = function () {
      var this$1 = this;

    var start = this.pos;
    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
    return this.pos > start
  };
  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
  StringStream.prototype.skipTo = function (ch) {
    var found = this.string.indexOf(ch, this.pos);
    if (found > -1) {this.pos = found; return true}
  };
  StringStream.prototype.backUp = function (n) {this.pos -= n;};
  StringStream.prototype.column = function () {
    if (this.lastColumnPos < this.start) {
      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
      this.lastColumnPos = this.start;
    }
    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
  };
  StringStream.prototype.indentation = function () {
    return countColumn(this.string, null, this.tabSize) -
      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
  };
  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
    if (typeof pattern == "string") {
      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
      var substr = this.string.substr(this.pos, pattern.length);
      if (cased(substr) == cased(pattern)) {
        if (consume !== false) { this.pos += pattern.length; }
        return true
      }
    } else {
      var match = this.string.slice(this.pos).match(pattern);
      if (match && match.index > 0) { return null }
      if (match && consume !== false) { this.pos += match[0].length; }
      return match
    }
  };
  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
  StringStream.prototype.hideFirstChars = function (n, inner) {
    this.lineStart += n;
    try { return inner() }
    finally { this.lineStart -= n; }
  };
  StringStream.prototype.lookAhead = function (n) {
    var oracle = this.lineOracle;
    return oracle && oracle.lookAhead(n)
  };
  StringStream.prototype.baseToken = function () {
    var oracle = this.lineOracle;
    return oracle && oracle.baseToken(this.pos)
  };

  // Find the line object corresponding to the given line number.
  function getLine(doc, n) {
    n -= doc.first;
    if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
    var chunk = doc;
    while (!chunk.lines) {
      for (var i = 0;; ++i) {
        var child = chunk.children[i], sz = child.chunkSize();
        if (n < sz) { chunk = child; break }
        n -= sz;
      }
    }
    return chunk.lines[n]
  }

  // Get the part of a document between two positions, as an array of
  // strings.
  function getBetween(doc, start, end) {
    var out = [], n = start.line;
    doc.iter(start.line, end.line + 1, function (line) {
      var text = line.text;
      if (n == end.line) { text = text.slice(0, end.ch); }
      if (n == start.line) { text = text.slice(start.ch); }
      out.push(text);
      ++n;
    });
    return out
  }
  // Get the lines between from and to, as array of strings.
  function getLines(doc, from, to) {
    var out = [];
    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
    return out
  }

  // Update the height of a line, propagating the height change
  // upwards to parent nodes.
  function updateLineHeight(line, height) {
    var diff = height - line.height;
    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
  }

  // Given a line object, find its line number by walking up through
  // its parent links.
  function lineNo(line) {
    if (line.parent == null) { return null }
    var cur = line.parent, no = indexOf(cur.lines, line);
    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
      for (var i = 0;; ++i) {
        if (chunk.children[i] == cur) { break }
        no += chunk.children[i].chunkSize();
      }
    }
    return no + cur.first
  }

  // Find the line at the given vertical position, using the height
  // information in the document tree.
  function lineAtHeight(chunk, h) {
    var n = chunk.first;
    outer: do {
      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
        var child = chunk.children[i$1], ch = child.height;
        if (h < ch) { chunk = child; continue outer }
        h -= ch;
        n += child.chunkSize();
      }
      return n
    } while (!chunk.lines)
    var i = 0;
    for (; i < chunk.lines.length; ++i) {
      var line = chunk.lines[i], lh = line.height;
      if (h < lh) { break }
      h -= lh;
    }
    return n + i
  }

  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}

  function lineNumberFor(options, i) {
    return String(options.lineNumberFormatter(i + options.firstLineNumber))
  }

  // A Pos instance represents a position within the text.
  function Pos(line, ch, sticky) {
    if ( sticky === void 0 ) sticky = null;

    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
    this.line = line;
    this.ch = ch;
    this.sticky = sticky;
  }

  // Compare two positions, return 0 if they are the same, a negative
  // number when a is less, and a positive number otherwise.
  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }

  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }

  function copyPos(x) {return Pos(x.line, x.ch)}
  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }

  // Most of the external API clips given positions to make sure they
  // actually exist within the document.
  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
  function clipPos(doc, pos) {
    if (pos.line < doc.first) { return Pos(doc.first, 0) }
    var last = doc.first + doc.size - 1;
    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
    return clipToLen(pos, getLine(doc, pos.line).text.length)
  }
  function clipToLen(pos, linelen) {
    var ch = pos.ch;
    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
    else if (ch < 0) { return Pos(pos.line, 0) }
    else { return pos }
  }
  function clipPosArray(doc, array) {
    var out = [];
    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
    return out
  }

  var SavedContext = function(state, lookAhead) {
    this.state = state;
    this.lookAhead = lookAhead;
  };

  var Context = function(doc, state, line, lookAhead) {
    this.state = state;
    this.doc = doc;
    this.line = line;
    this.maxLookAhead = lookAhead || 0;
    this.baseTokens = null;
    this.baseTokenPos = 1;
  };

  Context.prototype.lookAhead = function (n) {
    var line = this.doc.getLine(this.line + n);
    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
    return line
  };

  Context.prototype.baseToken = function (n) {
      var this$1 = this;

    if (!this.baseTokens) { return null }
    while (this.baseTokens[this.baseTokenPos] <= n)
      { this$1.baseTokenPos += 2; }
    var type = this.baseTokens[this.baseTokenPos + 1];
    return {type: type && type.replace(/( |^)overlay .*/, ""),
            size: this.baseTokens[this.baseTokenPos] - n}
  };

  Context.prototype.nextLine = function () {
    this.line++;
    if (this.maxLookAhead > 0) { this.maxLookAhead--; }
  };

  Context.fromSaved = function (doc, saved, line) {
    if (saved instanceof SavedContext)
      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
    else
      { return new Context(doc, copyState(doc.mode, saved), line) }
  };

  Context.prototype.save = function (copy) {
    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
  };


  // Compute a style array (an array starting with a mode generation
  // -- for invalidation -- followed by pairs of end positions and
  // style strings), which is used to highlight the tokens on the
  // line.
  function highlightLine(cm, line, context, forceToEnd) {
    // A styles array always starts with a number identifying the
    // mode/overlays that it is based on (for easy invalidation).
    var st = [cm.state.modeGen], lineClasses = {};
    // Compute the base array of styles
    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
            lineClasses, forceToEnd);
    var state = context.state;

    // Run overlays, adjust style array.
    var loop = function ( o ) {
      context.baseTokens = st;
      var overlay = cm.state.overlays[o], i = 1, at = 0;
      context.state = true;
      runMode(cm, line.text, overlay.mode, context, function (end, style) {
        var start = i;
        // Ensure there's a token end at the current position, and that i points at it
        while (at < end) {
          var i_end = st[i];
          if (i_end > end)
            { st.splice(i, 1, end, st[i+1], i_end); }
          i += 2;
          at = Math.min(end, i_end);
        }
        if (!style) { return }
        if (overlay.opaque) {
          st.splice(start, i - start, end, "overlay " + style);
          i = start + 2;
        } else {
          for (; start < i; start += 2) {
            var cur = st[start+1];
            st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
          }
        }
      }, lineClasses);
      context.state = state;
      context.baseTokens = null;
      context.baseTokenPos = 1;
    };

    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );

    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
  }

  function getLineStyles(cm, line, updateFrontier) {
    if (!line.styles || line.styles[0] != cm.state.modeGen) {
      var context = getContextBefore(cm, lineNo(line));
      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
      var result = highlightLine(cm, line, context);
      if (resetState) { context.state = resetState; }
      line.stateAfter = context.save(!resetState);
      line.styles = result.styles;
      if (result.classes) { line.styleClasses = result.classes; }
      else if (line.styleClasses) { line.styleClasses = null; }
      if (updateFrontier === cm.doc.highlightFrontier)
        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
    }
    return line.styles
  }

  function getContextBefore(cm, n, precise) {
    var doc = cm.doc, display = cm.display;
    if (!doc.mode.startState) { return new Context(doc, true, n) }
    var start = findStartLine(cm, n, precise);
    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);

    doc.iter(start, n, function (line) {
      processLine(cm, line.text, context);
      var pos = context.line;
      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
      context.nextLine();
    });
    if (precise) { doc.modeFrontier = context.line; }
    return context
  }

  // Lightweight form of highlight -- proceed over this line and
  // update state, but don't save a style array. Used for lines that
  // aren't currently visible.
  function processLine(cm, text, context, startAt) {
    var mode = cm.doc.mode;
    var stream = new StringStream(text, cm.options.tabSize, context);
    stream.start = stream.pos = startAt || 0;
    if (text == "") { callBlankLine(mode, context.state); }
    while (!stream.eol()) {
      readToken(mode, stream, context.state);
      stream.start = stream.pos;
    }
  }

  function callBlankLine(mode, state) {
    if (mode.blankLine) { return mode.blankLine(state) }
    if (!mode.innerMode) { return }
    var inner = innerMode(mode, state);
    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
  }

  function readToken(mode, stream, state, inner) {
    for (var i = 0; i < 10; i++) {
      if (inner) { inner[0] = innerMode(mode, state).mode; }
      var style = mode.token(stream, state);
      if (stream.pos > stream.start) { return style }
    }
    throw new Error("Mode " + mode.name + " failed to advance stream.")
  }

  var Token = function(stream, type, state) {
    this.start = stream.start; this.end = stream.pos;
    this.string = stream.current();
    this.type = type || null;
    this.state = state;
  };

  // Utility for getTokenAt and getLineTokens
  function takeToken(cm, pos, precise, asArray) {
    var doc = cm.doc, mode = doc.mode, style;
    pos = clipPos(doc, pos);
    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
    if (asArray) { tokens = []; }
    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
      stream.start = stream.pos;
      style = readToken(mode, stream, context.state);
      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
    }
    return asArray ? tokens : new Token(stream, style, context.state)
  }

  function extractLineClasses(type, output) {
    if (type) { for (;;) {
      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
      if (!lineClass) { break }
      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
      var prop = lineClass[1] ? "bgClass" : "textClass";
      if (output[prop] == null)
        { output[prop] = lineClass[2]; }
      else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
        { output[prop] += " " + lineClass[2]; }
    } }
    return type
  }

  // Run the given mode's parser over a line, calling f for each token.
  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
    var flattenSpans = mode.flattenSpans;
    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
    var curStart = 0, curStyle = null;
    var stream = new StringStream(text, cm.options.tabSize, context), style;
    var inner = cm.options.addModeClass && [null];
    if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
    while (!stream.eol()) {
      if (stream.pos > cm.options.maxHighlightLength) {
        flattenSpans = false;
        if (forceToEnd) { processLine(cm, text, context, stream.pos); }
        stream.pos = text.length;
        style = null;
      } else {
        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
      }
      if (inner) {
        var mName = inner[0].name;
        if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
      }
      if (!flattenSpans || curStyle != style) {
        while (curStart < stream.start) {
          curStart = Math.min(stream.start, curStart + 5000);
          f(curStart, curStyle);
        }
        curStyle = style;
      }
      stream.start = stream.pos;
    }
    while (curStart < stream.pos) {
      // Webkit seems to refuse to render text nodes longer than 57444
      // characters, and returns inaccurate measurements in nodes
      // starting around 5000 chars.
      var pos = Math.min(stream.pos, curStart + 5000);
      f(pos, curStyle);
      curStart = pos;
    }
  }

  // Finds the line to start with when starting a parse. Tries to
  // find a line with a stateAfter, so that it can start with a
  // valid state. If that fails, it returns the line with the
  // smallest indentation, which tends to need the least context to
  // parse correctly.
  function findStartLine(cm, n, precise) {
    var minindent, minline, doc = cm.doc;
    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
    for (var search = n; search > lim; --search) {
      if (search <= doc.first) { return doc.first }
      var line = getLine(doc, search - 1), after = line.stateAfter;
      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
        { return search }
      var indented = countColumn(line.text, null, cm.options.tabSize);
      if (minline == null || minindent > indented) {
        minline = search - 1;
        minindent = indented;
      }
    }
    return minline
  }

  function retreatFrontier(doc, n) {
    doc.modeFrontier = Math.min(doc.modeFrontier, n);
    if (doc.highlightFrontier < n - 10) { return }
    var start = doc.first;
    for (var line = n - 1; line > start; line--) {
      var saved = getLine(doc, line).stateAfter;
      // change is on 3
      // state on line 1 looked ahead 2 -- so saw 3
      // test 1 + 2 < 3 should cover this
      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
        start = line + 1;
        break
      }
    }
    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
  }

  // Optimize some code when these features are not used.
  var sawReadOnlySpans = false, sawCollapsedSpans = false;

  function seeReadOnlySpans() {
    sawReadOnlySpans = true;
  }

  function seeCollapsedSpans() {
    sawCollapsedSpans = true;
  }

  // TEXTMARKER SPANS

  function MarkedSpan(marker, from, to) {
    this.marker = marker;
    this.from = from; this.to = to;
  }

  // Search an array of spans for a span matching the given marker.
  function getMarkedSpanFor(spans, marker) {
    if (spans) { for (var i = 0; i < spans.length; ++i) {
      var span = spans[i];
      if (span.marker == marker) { return span }
    } }
  }
  // Remove a span from an array, returning undefined if no spans are
  // left (we don't store arrays for lines without spans).
  function removeMarkedSpan(spans, span) {
    var r;
    for (var i = 0; i < spans.length; ++i)
      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
    return r
  }
  // Add a span to a line.
  function addMarkedSpan(line, span) {
    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
    span.marker.attachLine(line);
  }

  // Used for the algorithm that adjusts markers for a change in the
  // document. These functions cut an array of spans at a given
  // character position, returning an array of remaining chunks (or
  // undefined if nothing remains).
  function markedSpansBefore(old, startCh, isInsert) {
    var nw;
    if (old) { for (var i = 0; i < old.length; ++i) {
      var span = old[i], marker = span.marker;
      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
      }
    } }
    return nw
  }
  function markedSpansAfter(old, endCh, isInsert) {
    var nw;
    if (old) { for (var i = 0; i < old.length; ++i) {
      var span = old[i], marker = span.marker;
      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
                                              span.to == null ? null : span.to - endCh));
      }
    } }
    return nw
  }

  // Given a change object, compute the new set of marker spans that
  // cover the line in which the change took place. Removes spans
  // entirely within the change, reconnects spans belonging to the
  // same marker that appear on both sides of the change, and cuts off
  // spans partially within the change. Returns an array of span
  // arrays with one element for each line in (after) the change.
  function stretchSpansOverChange(doc, change) {
    if (change.full) { return null }
    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
    if (!oldFirst && !oldLast) { return null }

    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
    // Get the spans that 'stick out' on both sides
    var first = markedSpansBefore(oldFirst, startCh, isInsert);
    var last = markedSpansAfter(oldLast, endCh, isInsert);

    // Next, merge those two ends
    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
    if (first) {
      // Fix up .to properties of first
      for (var i = 0; i < first.length; ++i) {
        var span = first[i];
        if (span.to == null) {
          var found = getMarkedSpanFor(last, span.marker);
          if (!found) { span.to = startCh; }
          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
        }
      }
    }
    if (last) {
      // Fix up .from in last (or move them into first in case of sameLine)
      for (var i$1 = 0; i$1 < last.length; ++i$1) {
        var span$1 = last[i$1];
        if (span$1.to != null) { span$1.to += offset; }
        if (span$1.from == null) {
          var found$1 = getMarkedSpanFor(first, span$1.marker);
          if (!found$1) {
            span$1.from = offset;
            if (sameLine) { (first || (first = [])).push(span$1); }
          }
        } else {
          span$1.from += offset;
          if (sameLine) { (first || (first = [])).push(span$1); }
        }
      }
    }
    // Make sure we didn't create any zero-length spans
    if (first) { first = clearEmptySpans(first); }
    if (last && last != first) { last = clearEmptySpans(last); }

    var newMarkers = [first];
    if (!sameLine) {
      // Fill gap with whole-line-spans
      var gap = change.text.length - 2, gapMarkers;
      if (gap > 0 && first)
        { for (var i$2 = 0; i$2 < first.length; ++i$2)
          { if (first[i$2].to == null)
            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
      for (var i$3 = 0; i$3 < gap; ++i$3)
        { newMarkers.push(gapMarkers); }
      newMarkers.push(last);
    }
    return newMarkers
  }

  // Remove spans that are empty and don't have a clearWhenEmpty
  // option of false.
  function clearEmptySpans(spans) {
    for (var i = 0; i < spans.length; ++i) {
      var span = spans[i];
      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
        { spans.splice(i--, 1); }
    }
    if (!spans.length) { return null }
    return spans
  }

  // Used to 'clip' out readOnly ranges when making a change.
  function removeReadOnlyRanges(doc, from, to) {
    var markers = null;
    doc.iter(from.line, to.line + 1, function (line) {
      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
        var mark = line.markedSpans[i].marker;
        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
          { (markers || (markers = [])).push(mark); }
      } }
    });
    if (!markers) { return null }
    var parts = [{from: from, to: to}];
    for (var i = 0; i < markers.length; ++i) {
      var mk = markers[i], m = mk.find(0);
      for (var j = 0; j < parts.length; ++j) {
        var p = parts[j];
        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
          { newParts.push({from: p.from, to: m.from}); }
        if (dto > 0 || !mk.inclusiveRight && !dto)
          { newParts.push({from: m.to, to: p.to}); }
        parts.splice.apply(parts, newParts);
        j += newParts.length - 3;
      }
    }
    return parts
  }

  // Connect or disconnect spans from a line.
  function detachMarkedSpans(line) {
    var spans = line.markedSpans;
    if (!spans) { return }
    for (var i = 0; i < spans.length; ++i)
      { spans[i].marker.detachLine(line); }
    line.markedSpans = null;
  }
  function attachMarkedSpans(line, spans) {
    if (!spans) { return }
    for (var i = 0; i < spans.length; ++i)
      { spans[i].marker.attachLine(line); }
    line.markedSpans = spans;
  }

  // Helpers used when computing which overlapping collapsed span
  // counts as the larger one.
  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }

  // Returns a number indicating which of two overlapping collapsed
  // spans is larger (and thus includes the other). Falls back to
  // comparing ids when the spans cover exactly the same range.
  function compareCollapsedMarkers(a, b) {
    var lenDiff = a.lines.length - b.lines.length;
    if (lenDiff != 0) { return lenDiff }
    var aPos = a.find(), bPos = b.find();
    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
    if (fromCmp) { return -fromCmp }
    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
    if (toCmp) { return toCmp }
    return b.id - a.id
  }

  // Find out whether a line ends or starts in a collapsed span. If
  // so, return the marker for that span.
  function collapsedSpanAtSide(line, start) {
    var sps = sawCollapsedSpans && line.markedSpans, found;
    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
      sp = sps[i];
      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
        { found = sp.marker; }
    } }
    return found
  }
  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }

  function collapsedSpanAround(line, ch) {
    var sps = sawCollapsedSpans && line.markedSpans, found;
    if (sps) { for (var i = 0; i < sps.length; ++i) {
      var sp = sps[i];
      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
    } }
    return found
  }

  // Test whether there exists a collapsed span that partially
  // overlaps (covers the start or end, but not both) of a new span.
  // Such overlap is not allowed.
  function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
    var line = getLine(doc, lineNo$$1);
    var sps = sawCollapsedSpans && line.markedSpans;
    if (sps) { for (var i = 0; i < sps.length; ++i) {
      var sp = sps[i];
      if (!sp.marker.collapsed) { continue }
      var found = sp.marker.find(0);
      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
        { return true }
    } }
  }

  // A visual line is a line as drawn on the screen. Folding, for
  // example, can cause multiple logical lines to appear on the same
  // visual line. This finds the start of the visual line that the
  // given line is part of (usually that is the line itself).
  function visualLine(line) {
    var merged;
    while (merged = collapsedSpanAtStart(line))
      { line = merged.find(-1, true).line; }
    return line
  }

  function visualLineEnd(line) {
    var merged;
    while (merged = collapsedSpanAtEnd(line))
      { line = merged.find(1, true).line; }
    return line
  }

  // Returns an array of logical lines that continue the visual line
  // started by the argument, or undefined if there are no such lines.
  function visualLineContinued(line) {
    var merged, lines;
    while (merged = collapsedSpanAtEnd(line)) {
      line = merged.find(1, true).line
      ;(lines || (lines = [])).push(line);
    }
    return lines
  }

  // Get the line number of the start of the visual line that the
  // given line number is part of.
  function visualLineNo(doc, lineN) {
    var line = getLine(doc, lineN), vis = visualLine(line);
    if (line == vis) { return lineN }
    return lineNo(vis)
  }

  // Get the line number of the start of the next visual line after
  // the given line.
  function visualLineEndNo(doc, lineN) {
    if (lineN > doc.lastLine()) { return lineN }
    var line = getLine(doc, lineN), merged;
    if (!lineIsHidden(doc, line)) { return lineN }
    while (merged = collapsedSpanAtEnd(line))
      { line = merged.find(1, true).line; }
    return lineNo(line) + 1
  }

  // Compute whether a line is hidden. Lines count as hidden when they
  // are part of a visual line that starts with another line, or when
  // they are entirely covered by collapsed, non-widget span.
  function lineIsHidden(doc, line) {
    var sps = sawCollapsedSpans && line.markedSpans;
    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
      sp = sps[i];
      if (!sp.marker.collapsed) { continue }
      if (sp.from == null) { return true }
      if (sp.marker.widgetNode) { continue }
      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
        { return true }
    } }
  }
  function lineIsHiddenInner(doc, line, span) {
    if (span.to == null) {
      var end = span.marker.find(1, true);
      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
    }
    if (span.marker.inclusiveRight && span.to == line.text.length)
      { return true }
    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
      sp = line.markedSpans[i];
      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
          (sp.to == null || sp.to != span.from) &&
          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
          lineIsHiddenInner(doc, line, sp)) { return true }
    }
  }

  // Find the height above the given line.
  function heightAtLine(lineObj) {
    lineObj = visualLine(lineObj);

    var h = 0, chunk = lineObj.parent;
    for (var i = 0; i < chunk.lines.length; ++i) {
      var line = chunk.lines[i];
      if (line == lineObj) { break }
      else { h += line.height; }
    }
    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
        var cur = p.children[i$1];
        if (cur == chunk) { break }
        else { h += cur.height; }
      }
    }
    return h
  }

  // Compute the character length of a line, taking into account
  // collapsed ranges (see markText) that might hide parts, and join
  // other lines onto it.
  function lineLength(line) {
    if (line.height == 0) { return 0 }
    var len = line.text.length, merged, cur = line;
    while (merged = collapsedSpanAtStart(cur)) {
      var found = merged.find(0, true);
      cur = found.from.line;
      len += found.from.ch - found.to.ch;
    }
    cur = line;
    while (merged = collapsedSpanAtEnd(cur)) {
      var found$1 = merged.find(0, true);
      len -= cur.text.length - found$1.from.ch;
      cur = found$1.to.line;
      len += cur.text.length - found$1.to.ch;
    }
    return len
  }

  // Find the longest line in the document.
  function findMaxLine(cm) {
    var d = cm.display, doc = cm.doc;
    d.maxLine = getLine(doc, doc.first);
    d.maxLineLength = lineLength(d.maxLine);
    d.maxLineChanged = true;
    doc.iter(function (line) {
      var len = lineLength(line);
      if (len > d.maxLineLength) {
        d.maxLineLength = len;
        d.maxLine = line;
      }
    });
  }

  // LINE DATA STRUCTURE

  // Line objects. These hold state related to a line, including
  // highlighting info (the styles array).
  var Line = function(text, markedSpans, estimateHeight) {
    this.text = text;
    attachMarkedSpans(this, markedSpans);
    this.height = estimateHeight ? estimateHeight(this) : 1;
  };

  Line.prototype.lineNo = function () { return lineNo(this) };
  eventMixin(Line);

  // Change the content (text, markers) of a line. Automatically
  // invalidates cached information and tries to re-estimate the
  // line's height.
  function updateLine(line, text, markedSpans, estimateHeight) {
    line.text = text;
    if (line.stateAfter) { line.stateAfter = null; }
    if (line.styles) { line.styles = null; }
    if (line.order != null) { line.order = null; }
    detachMarkedSpans(line);
    attachMarkedSpans(line, markedSpans);
    var estHeight = estimateHeight ? estimateHeight(line) : 1;
    if (estHeight != line.height) { updateLineHeight(line, estHeight); }
  }

  // Detach a line from the document tree and its markers.
  function cleanUpLine(line) {
    line.parent = null;
    detachMarkedSpans(line);
  }

  // Convert a style as returned by a mode (either null, or a string
  // containing one or more styles) to a CSS style. This is cached,
  // and also looks for line-wide styles.
  var styleToClassCache = {}, styleToClassCacheWithMode = {};
  function interpretTokenStyle(style, options) {
    if (!style || /^\s*$/.test(style)) { return null }
    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
    return cache[style] ||
      (cache[style] = style.replace(/\S+/g, "cm-$&"))
  }

  // Render the DOM representation of the text of a line. Also builds
  // up a 'line map', which points at the DOM nodes that represent
  // specific stretches of text, and is used by the measuring code.
  // The returned object contains the DOM node, this map, and
  // information about line-wide styles that were set by the mode.
  function buildLineContent(cm, lineView) {
    // The padding-right forces the element to have a 'border', which
    // is needed on Webkit to be able to get line-level bounding
    // rectangles for it (in measureChar).
    var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
    var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
                   col: 0, pos: 0, cm: cm,
                   trailingSpace: false,
                   splitSpaces: cm.getOption("lineWrapping")};
    lineView.measure = {};

    // Iterate over the logical lines that make up this visual line.
    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
      builder.pos = 0;
      builder.addToken = buildToken;
      // Optionally wire in some hacks into the token-rendering
      // algorithm, to deal with browser quirks.
      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
      builder.map = [];
      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
      if (line.styleClasses) {
        if (line.styleClasses.bgClass)
          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
        if (line.styleClasses.textClass)
          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
      }

      // Ensure at least a single node is present, for measuring.
      if (builder.map.length == 0)
        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }

      // Store the map and a cache object for the current logical line
      if (i == 0) {
        lineView.measure.map = builder.map;
        lineView.measure.cache = {};
      } else {
  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
      }
    }

    // See issue #2901
    if (webkit) {
      var last = builder.content.lastChild;
      if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
        { builder.content.className = "cm-tab-wrap-hack"; }
    }

    signal(cm, "renderLine", cm, lineView.line, builder.pre);
    if (builder.pre.className)
      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }

    return builder
  }

  function defaultSpecialCharPlaceholder(ch) {
    var token = elt("span", "\u2022", "cm-invalidchar");
    token.title = "\\u" + ch.charCodeAt(0).toString(16);
    token.setAttribute("aria-label", token.title);
    return token
  }

  // Build up the DOM representation for a single token, and add it to
  // the line map. Takes care to render special characters separately.
  function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
    if (!text) { return }
    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
    var special = builder.cm.state.specialChars, mustWrap = false;
    var content;
    if (!special.test(text)) {
      builder.col += text.length;
      content = document.createTextNode(displayText);
      builder.map.push(builder.pos, builder.pos + text.length, content);
      if (ie && ie_version < 9) { mustWrap = true; }
      builder.pos += text.length;
    } else {
      content = document.createDocumentFragment();
      var pos = 0;
      while (true) {
        special.lastIndex = pos;
        var m = special.exec(text);
        var skipped = m ? m.index - pos : text.length - pos;
        if (skipped) {
          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
          else { content.appendChild(txt); }
          builder.map.push(builder.pos, builder.pos + skipped, txt);
          builder.col += skipped;
          builder.pos += skipped;
        }
        if (!m) { break }
        pos += skipped + 1;
        var txt$1 = (void 0);
        if (m[0] == "\t") {
          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
          txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
          txt$1.setAttribute("role", "presentation");
          txt$1.setAttribute("cm-text", "\t");
          builder.col += tabWidth;
        } else if (m[0] == "\r" || m[0] == "\n") {
          txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
          txt$1.setAttribute("cm-text", m[0]);
          builder.col += 1;
        } else {
          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
          txt$1.setAttribute("cm-text", m[0]);
          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
          else { content.appendChild(txt$1); }
          builder.col += 1;
        }
        builder.map.push(builder.pos, builder.pos + 1, txt$1);
        builder.pos++;
      }
    }
    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
    if (style || startStyle || endStyle || mustWrap || css) {
      var fullStyle = style || "";
      if (startStyle) { fullStyle += startStyle; }
      if (endStyle) { fullStyle += endStyle; }
      var token = elt("span", [content], fullStyle, css);
      if (attributes) {
        for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
          { token.setAttribute(attr, attributes[attr]); } }
      }
      return builder.content.appendChild(token)
    }
    builder.content.appendChild(content);
  }

  // Change some spaces to NBSP to prevent the browser from collapsing
  // trailing spaces at the end of a line when rendering text (issue #1362).
  function splitSpaces(text, trailingBefore) {
    if (text.length > 1 && !/  /.test(text)) { return text }
    var spaceBefore = trailingBefore, result = "";
    for (var i = 0; i < text.length; i++) {
      var ch = text.charAt(i);
      if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
        { ch = "\u00a0"; }
      result += ch;
      spaceBefore = ch == " ";
    }
    return result
  }

  // Work around nonsense dimensions being reported for stretches of
  // right-to-left text.
  function buildTokenBadBidi(inner, order) {
    return function (builder, text, style, startStyle, endStyle, css, attributes) {
      style = style ? style + " cm-force-border" : "cm-force-border";
      var start = builder.pos, end = start + text.length;
      for (;;) {
        // Find the part that overlaps with the start of this text
        var part = (void 0);
        for (var i = 0; i < order.length; i++) {
          part = order[i];
          if (part.to > start && part.from <= start) { break }
        }
        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
        inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
        startStyle = null;
        text = text.slice(part.to - start);
        start = part.to;
      }
    }
  }

  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
    var widget = !ignoreWidget && marker.widgetNode;
    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
      if (!widget)
        { widget = builder.content.appendChild(document.createElement("span")); }
      widget.setAttribute("cm-marker", marker.id);
    }
    if (widget) {
      builder.cm.display.input.setUneditable(widget);
      builder.content.appendChild(widget);
    }
    builder.pos += size;
    builder.trailingSpace = false;
  }

  // Outputs a number of spans to make up a line, taking highlighting
  // and marked text into account.
  function insertLineContent(line, builder, styles) {
    var spans = line.markedSpans, allText = line.text, at = 0;
    if (!spans) {
      for (var i$1 = 1; i$1 < styles.length; i$1+=2)
        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
      return
    }

    var len = allText.length, pos = 0, i = 1, text = "", style, css;
    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
    for (;;) {
      if (nextChange == pos) { // Update current marker set
        spanStyle = spanEndStyle = spanStartStyle = css = "";
        attributes = null;
        collapsed = null; nextChange = Infinity;
        var foundBookmarks = [], endStyles = (void 0);
        for (var j = 0; j < spans.length; ++j) {
          var sp = spans[j], m = sp.marker;
          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
            foundBookmarks.push(m);
          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
              nextChange = sp.to;
              spanEndStyle = "";
            }
            if (m.className) { spanStyle += " " + m.className; }
            if (m.css) { css = (css ? css + ";" : "") + m.css; }
            if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
            // support for the old title property
            // https://github.com/codemirror/CodeMirror/pull/5673
            if (m.title) { (attributes || (attributes = {})).title = m.title; }
            if (m.attributes) {
              for (var attr in m.attributes)
                { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
            }
            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
              { collapsed = sp; }
          } else if (sp.from > pos && nextChange > sp.from) {
            nextChange = sp.from;
          }
        }
        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }

        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
        if (collapsed && (collapsed.from || 0) == pos) {
          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
                             collapsed.marker, collapsed.from == null);
          if (collapsed.to == null) { return }
          if (collapsed.to == pos) { collapsed = false; }
        }
      }
      if (pos >= len) { break }

      var upto = Math.min(len, nextChange);
      while (true) {
        if (text) {
          var end = pos + text.length;
          if (!collapsed) {
            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
          }
          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
          pos = end;
          spanStartStyle = "";
        }
        text = allText.slice(at, at = styles[i++]);
        style = interpretTokenStyle(styles[i++], builder.cm.options);
      }
    }
  }


  // These objects are used to represent the visible (currently drawn)
  // part of the document. A LineView may correspond to multiple
  // logical lines, if those are connected by collapsed ranges.
  function LineView(doc, line, lineN) {
    // The starting line
    this.line = line;
    // Continuing lines, if any
    this.rest = visualLineContinued(line);
    // Number of logical lines in this visual line
    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
    this.node = this.text = null;
    this.hidden = lineIsHidden(doc, line);
  }

  // Create a range of LineView objects for the given lines.
  function buildViewArray(cm, from, to) {
    var array = [], nextPos;
    for (var pos = from; pos < to; pos = nextPos) {
      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
      nextPos = pos + view.size;
      array.push(view);
    }
    return array
  }

  var operationGroup = null;

  function pushOperation(op) {
    if (operationGroup) {
      operationGroup.ops.push(op);
    } else {
      op.ownsGroup = operationGroup = {
        ops: [op],
        delayedCallbacks: []
      };
    }
  }

  function fireCallbacksForOps(group) {
    // Calls delayed callbacks and cursorActivity handlers until no
    // new ones appear
    var callbacks = group.delayedCallbacks, i = 0;
    do {
      for (; i < callbacks.length; i++)
        { callbacks[i].call(null); }
      for (var j = 0; j < group.ops.length; j++) {
        var op = group.ops[j];
        if (op.cursorActivityHandlers)
          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
      }
    } while (i < callbacks.length)
  }

  function finishOperation(op, endCb) {
    var group = op.ownsGroup;
    if (!group) { return }

    try { fireCallbacksForOps(group); }
    finally {
      operationGroup = null;
      endCb(group);
    }
  }

  var orphanDelayedCallbacks = null;

  // Often, we want to signal events at a point where we are in the
  // middle of some work, but don't want the handler to start calling
  // other methods on the editor, which might be in an inconsistent
  // state or simply not expect any other events to happen.
  // signalLater looks whether there are any handlers, and schedules
  // them to be executed when the last operation ends, or, if no
  // operation is active, when a timeout fires.
  function signalLater(emitter, type /*, values...*/) {
    var arr = getHandlers(emitter, type);
    if (!arr.length) { return }
    var args = Array.prototype.slice.call(arguments, 2), list;
    if (operationGroup) {
      list = operationGroup.delayedCallbacks;
    } else if (orphanDelayedCallbacks) {
      list = orphanDelayedCallbacks;
    } else {
      list = orphanDelayedCallbacks = [];
      setTimeout(fireOrphanDelayed, 0);
    }
    var loop = function ( i ) {
      list.push(function () { return arr[i].apply(null, args); });
    };

    for (var i = 0; i < arr.length; ++i)
      loop( i );
  }

  function fireOrphanDelayed() {
    var delayed = orphanDelayedCallbacks;
    orphanDelayedCallbacks = null;
    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
  }

  // When an aspect of a line changes, a string is added to
  // lineView.changes. This updates the relevant part of the line's
  // DOM structure.
  function updateLineForChanges(cm, lineView, lineN, dims) {
    for (var j = 0; j < lineView.changes.length; j++) {
      var type = lineView.changes[j];
      if (type == "text") { updateLineText(cm, lineView); }
      else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
      else if (type == "class") { updateLineClasses(cm, lineView); }
      else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
    }
    lineView.changes = null;
  }

  // Lines with gutter elements, widgets or a background class need to
  // be wrapped, and have the extra elements added to the wrapper div
  function ensureLineWrapped(lineView) {
    if (lineView.node == lineView.text) {
      lineView.node = elt("div", null, null, "position: relative");
      if (lineView.text.parentNode)
        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
      lineView.node.appendChild(lineView.text);
      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
    }
    return lineView.node
  }

  function updateLineBackground(cm, lineView) {
    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
    if (cls) { cls += " CodeMirror-linebackground"; }
    if (lineView.background) {
      if (cls) { lineView.background.className = cls; }
      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
    } else if (cls) {
      var wrap = ensureLineWrapped(lineView);
      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
      cm.display.input.setUneditable(lineView.background);
    }
  }

  // Wrapper around buildLineContent which will reuse the structure
  // in display.externalMeasured when possible.
  function getLineContent(cm, lineView) {
    var ext = cm.display.externalMeasured;
    if (ext && ext.line == lineView.line) {
      cm.display.externalMeasured = null;
      lineView.measure = ext.measure;
      return ext.built
    }
    return buildLineContent(cm, lineView)
  }

  // Redraw the line's text. Interacts with the background and text
  // classes because the mode may output tokens that influence these
  // classes.
  function updateLineText(cm, lineView) {
    var cls = lineView.text.className;
    var built = getLineContent(cm, lineView);
    if (lineView.text == lineView.node) { lineView.node = built.pre; }
    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
    lineView.text = built.pre;
    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
      lineView.bgClass = built.bgClass;
      lineView.textClass = built.textClass;
      updateLineClasses(cm, lineView);
    } else if (cls) {
      lineView.text.className = cls;
    }
  }

  function updateLineClasses(cm, lineView) {
    updateLineBackground(cm, lineView);
    if (lineView.line.wrapClass)
      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
    else if (lineView.node != lineView.text)
      { lineView.node.className = ""; }
    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
    lineView.text.className = textClass || "";
  }

  function updateLineGutter(cm, lineView, lineN, dims) {
    if (lineView.gutter) {
      lineView.node.removeChild(lineView.gutter);
      lineView.gutter = null;
    }
    if (lineView.gutterBackground) {
      lineView.node.removeChild(lineView.gutterBackground);
      lineView.gutterBackground = null;
    }
    if (lineView.line.gutterClass) {
      var wrap = ensureLineWrapped(lineView);
      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
                                      ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
      cm.display.input.setUneditable(lineView.gutterBackground);
      wrap.insertBefore(lineView.gutterBackground, lineView.text);
    }
    var markers = lineView.line.gutterMarkers;
    if (cm.options.lineNumbers || markers) {
      var wrap$1 = ensureLineWrapped(lineView);
      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
      cm.display.input.setUneditable(gutterWrap);
      wrap$1.insertBefore(gutterWrap, lineView.text);
      if (lineView.line.gutterClass)
        { gutterWrap.className += " " + lineView.line.gutterClass; }
      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
        { lineView.lineNumber = gutterWrap.appendChild(
          elt("div", lineNumberFor(cm.options, lineN),
              "CodeMirror-linenumber CodeMirror-gutter-elt",
              ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
      if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
        var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
        if (found)
          { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
                                     ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
      } }
    }
  }

  function updateLineWidgets(cm, lineView, dims) {
    if (lineView.alignable) { lineView.alignable = null; }
    var isWidget = classTest("CodeMirror-linewidget");
    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
      next = node.nextSibling;
      if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
    }
    insertLineWidgets(cm, lineView, dims);
  }

  // Build a line's DOM representation from scratch
  function buildLineElement(cm, lineView, lineN, dims) {
    var built = getLineContent(cm, lineView);
    lineView.text = lineView.node = built.pre;
    if (built.bgClass) { lineView.bgClass = built.bgClass; }
    if (built.textClass) { lineView.textClass = built.textClass; }

    updateLineClasses(cm, lineView);
    updateLineGutter(cm, lineView, lineN, dims);
    insertLineWidgets(cm, lineView, dims);
    return lineView.node
  }

  // A lineView may contain multiple logical lines (when merged by
  // collapsed spans). The widgets for all of them need to be drawn.
  function insertLineWidgets(cm, lineView, dims) {
    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
  }

  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
    if (!line.widgets) { return }
    var wrap = ensureLineWrapped(lineView);
    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
      if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
      positionLineWidget(widget, node, lineView, dims);
      cm.display.input.setUneditable(node);
      if (allowAbove && widget.above)
        { wrap.insertBefore(node, lineView.gutter || lineView.text); }
      else
        { wrap.appendChild(node); }
      signalLater(widget, "redraw");
    }
  }

  function positionLineWidget(widget, node, lineView, dims) {
    if (widget.noHScroll) {
  (lineView.alignable || (lineView.alignable = [])).push(node);
      var width = dims.wrapperWidth;
      node.style.left = dims.fixedPos + "px";
      if (!widget.coverGutter) {
        width -= dims.gutterTotalWidth;
        node.style.paddingLeft = dims.gutterTotalWidth + "px";
      }
      node.style.width = width + "px";
    }
    if (widget.coverGutter) {
      node.style.zIndex = 5;
      node.style.position = "relative";
      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
    }
  }

  function widgetHeight(widget) {
    if (widget.height != null) { return widget.height }
    var cm = widget.doc.cm;
    if (!cm) { return 0 }
    if (!contains(document.body, widget.node)) {
      var parentStyle = "position: relative;";
      if (widget.coverGutter)
        { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
      if (widget.noHScroll)
        { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
    }
    return widget.height = widget.node.parentNode.offsetHeight
  }

  // Return true when the given mouse event happened in a widget
  function eventInWidget(display, e) {
    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
          (n.parentNode == display.sizer && n != display.mover))
        { return true }
    }
  }

  // POSITION MEASUREMENT

  function paddingTop(display) {return display.lineSpace.offsetTop}
  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
  function paddingH(display) {
    if (display.cachedPaddingH) { return display.cachedPaddingH }
    var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
    return data
  }

  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
  function displayWidth(cm) {
    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
  }
  function displayHeight(cm) {
    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
  }

  // Ensure the lineView.wrapping.heights array is populated. This is
  // an array of bottom offsets for the lines that make up a drawn
  // line. When lineWrapping is on, there might be more than one
  // height.
  function ensureLineHeights(cm, lineView, rect) {
    var wrapping = cm.options.lineWrapping;
    var curWidth = wrapping && displayWidth(cm);
    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
      var heights = lineView.measure.heights = [];
      if (wrapping) {
        lineView.measure.width = curWidth;
        var rects = lineView.text.firstChild.getClientRects();
        for (var i = 0; i < rects.length - 1; i++) {
          var cur = rects[i], next = rects[i + 1];
          if (Math.abs(cur.bottom - next.bottom) > 2)
            { heights.push((cur.bottom + next.top) / 2 - rect.top); }
        }
      }
      heights.push(rect.bottom - rect.top);
    }
  }

  // Find a line map (mapping character offsets to text nodes) and a
  // measurement cache for the given line number. (A line view might
  // contain multiple lines when collapsed ranges are present.)
  function mapFromLineView(lineView, line, lineN) {
    if (lineView.line == line)
      { return {map: lineView.measure.map, cache: lineView.measure.cache} }
    for (var i = 0; i < lineView.rest.length; i++)
      { if (lineView.rest[i] == line)
        { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
    for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
      { if (lineNo(lineView.rest[i$1]) > lineN)
        { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
  }

  // Render a line into the hidden node display.externalMeasured. Used
  // when measurement is needed for a line that's not in the viewport.
  function updateExternalMeasurement(cm, line) {
    line = visualLine(line);
    var lineN = lineNo(line);
    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
    view.lineN = lineN;
    var built = view.built = buildLineContent(cm, view);
    view.text = built.pre;
    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
    return view
  }

  // Get a {top, bottom, left, right} box (in line-local coordinates)
  // for a given character.
  function measureChar(cm, line, ch, bias) {
    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
  }

  // Find a line view that corresponds to the given line number.
  function findViewForLine(cm, lineN) {
    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
      { return cm.display.view[findViewIndex(cm, lineN)] }
    var ext = cm.display.externalMeasured;
    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
      { return ext }
  }

  // Measurement can be split in two steps, the set-up work that
  // applies to the whole line, and the measurement of the actual
  // character. Functions like coordsChar, that need to do a lot of
  // measurements in a row, can thus ensure that the set-up work is
  // only done once.
  function prepareMeasureForLine(cm, line) {
    var lineN = lineNo(line);
    var view = findViewForLine(cm, lineN);
    if (view && !view.text) {
      view = null;
    } else if (view && view.changes) {
      updateLineForChanges(cm, view, lineN, getDimensions(cm));
      cm.curOp.forceUpdate = true;
    }
    if (!view)
      { view = updateExternalMeasurement(cm, line); }

    var info = mapFromLineView(view, line, lineN);
    return {
      line: line, view: view, rect: null,
      map: info.map, cache: info.cache, before: info.before,
      hasHeights: false
    }
  }

  // Given a prepared measurement object, measures the position of an
  // actual character (or fetches it from the cache).
  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
    if (prepared.before) { ch = -1; }
    var key = ch + (bias || ""), found;
    if (prepared.cache.hasOwnProperty(key)) {
      found = prepared.cache[key];
    } else {
      if (!prepared.rect)
        { prepared.rect = prepared.view.text.getBoundingClientRect(); }
      if (!prepared.hasHeights) {
        ensureLineHeights(cm, prepared.view, prepared.rect);
        prepared.hasHeights = true;
      }
      found = measureCharInner(cm, prepared, ch, bias);
      if (!found.bogus) { prepared.cache[key] = found; }
    }
    return {left: found.left, right: found.right,
            top: varHeight ? found.rtop : found.top,
            bottom: varHeight ? found.rbottom : found.bottom}
  }

  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};

  function nodeAndOffsetInLineMap(map$$1, ch, bias) {
    var node, start, end, collapse, mStart, mEnd;
    // First, search the line map for the text node corresponding to,
    // or closest to, the target character.
    for (var i = 0; i < map$$1.length; i += 3) {
      mStart = map$$1[i];
      mEnd = map$$1[i + 1];
      if (ch < mStart) {
        start = 0; end = 1;
        collapse = "left";
      } else if (ch < mEnd) {
        start = ch - mStart;
        end = start + 1;
      } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
        end = mEnd - mStart;
        start = end - 1;
        if (ch >= mEnd) { collapse = "right"; }
      }
      if (start != null) {
        node = map$$1[i + 2];
        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
          { collapse = bias; }
        if (bias == "left" && start == 0)
          { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
            node = map$$1[(i -= 3) + 2];
            collapse = "left";
          } }
        if (bias == "right" && start == mEnd - mStart)
          { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
            node = map$$1[(i += 3) + 2];
            collapse = "right";
          } }
        break
      }
    }
    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
  }

  function getUsefulRect(rects, bias) {
    var rect = nullRect;
    if (bias == "left") { for (var i = 0; i < rects.length; i++) {
      if ((rect = rects[i]).left != rect.right) { break }
    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
      if ((rect = rects[i$1]).left != rect.right) { break }
    } }
    return rect
  }

  function measureCharInner(cm, prepared, ch, bias) {
    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;

    var rect;
    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
          { rect = node.parentNode.getBoundingClientRect(); }
        else
          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
        if (rect.left || rect.right || start == 0) { break }
        end = start;
        start = start - 1;
        collapse = "right";
      }
      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
    } else { // If it is a widget, simply get the box for the whole widget.
      if (start > 0) { collapse = bias = "right"; }
      var rects;
      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
        { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
      else
        { rect = node.getBoundingClientRect(); }
    }
    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
      var rSpan = node.parentNode.getClientRects()[0];
      if (rSpan)
        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
      else
        { rect = nullRect; }
    }

    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
    var mid = (rtop + rbot) / 2;
    var heights = prepared.view.measure.heights;
    var i = 0;
    for (; i < heights.length - 1; i++)
      { if (mid < heights[i]) { break } }
    var top = i ? heights[i - 1] : 0, bot = heights[i];
    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
                  top: top, bottom: bot};
    if (!rect.left && !rect.right) { result.bogus = true; }
    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }

    return result
  }

  // Work around problem with bounding client rects on ranges being
  // returned incorrectly when zoomed on IE10 and below.
  function maybeUpdateRectForZooming(measure, rect) {
    if (!window.screen || screen.logicalXDPI == null ||
        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
      { return rect }
    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
    return {left: rect.left * scaleX, right: rect.right * scaleX,
            top: rect.top * scaleY, bottom: rect.bottom * scaleY}
  }

  function clearLineMeasurementCacheFor(lineView) {
    if (lineView.measure) {
      lineView.measure.cache = {};
      lineView.measure.heights = null;
      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
        { lineView.measure.caches[i] = {}; } }
    }
  }

  function clearLineMeasurementCache(cm) {
    cm.display.externalMeasure = null;
    removeChildren(cm.display.lineMeasure);
    for (var i = 0; i < cm.display.view.length; i++)
      { clearLineMeasurementCacheFor(cm.display.view[i]); }
  }

  function clearCaches(cm) {
    clearLineMeasurementCache(cm);
    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
    cm.display.lineNumChars = null;
  }

  function pageScrollX() {
    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
    // which causes page_Offset and bounding client rects to use
    // different reference viewports and invalidate our calculations.
    if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
    return window.pageXOffset || (document.documentElement || document.body).scrollLeft
  }
  function pageScrollY() {
    if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
    return window.pageYOffset || (document.documentElement || document.body).scrollTop
  }

  function widgetTopHeight(lineObj) {
    var height = 0;
    if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
      { height += widgetHeight(lineObj.widgets[i]); } } }
    return height
  }

  // Converts a {top, bottom, left, right} box from line-local
  // coordinates into another coordinate system. Context may be one of
  // "line", "div" (display.lineDiv), "local"./null (editor), "window",
  // or "page".
  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
    if (!includeWidgets) {
      var height = widgetTopHeight(lineObj);
      rect.top += height; rect.bottom += height;
    }
    if (context == "line") { return rect }
    if (!context) { context = "local"; }
    var yOff = heightAtLine(lineObj);
    if (context == "local") { yOff += paddingTop(cm.display); }
    else { yOff -= cm.display.viewOffset; }
    if (context == "page" || context == "window") {
      var lOff = cm.display.lineSpace.getBoundingClientRect();
      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
      rect.left += xOff; rect.right += xOff;
    }
    rect.top += yOff; rect.bottom += yOff;
    return rect
  }

  // Coverts a box from "div" coords to another coordinate system.
  // Context may be "window", "page", "div", or "local"./null.
  function fromCoordSystem(cm, coords, context) {
    if (context == "div") { return coords }
    var left = coords.left, top = coords.top;
    // First move into "page" coordinate system
    if (context == "page") {
      left -= pageScrollX();
      top -= pageScrollY();
    } else if (context == "local" || !context) {
      var localBox = cm.display.sizer.getBoundingClientRect();
      left += localBox.left;
      top += localBox.top;
    }

    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
  }

  function charCoords(cm, pos, context, lineObj, bias) {
    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
  }

  // Returns a box for a given cursor position, which may have an
  // 'other' property containing the position of the secondary cursor
  // on a bidi boundary.
  // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
  // and after `char - 1` in writing order of `char - 1`
  // A cursor Pos(line, char, "after") is on the same visual line as `char`
  // and before `char` in writing order of `char`
  // Examples (upper-case letters are RTL, lower-case are LTR):
  //     Pos(0, 1, ...)
  //     before   after
  // ab     a|b     a|b
  // aB     a|B     aB|
  // Ab     |Ab     A|b
  // AB     B|A     B|A
  // Every position after the last character on a line is considered to stick
  // to the last character on the line.
  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
    lineObj = lineObj || getLine(cm.doc, pos.line);
    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
    function get(ch, right) {
      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
      if (right) { m.left = m.right; } else { m.right = m.left; }
      return intoCoordSystem(cm, lineObj, m, context)
    }
    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
    if (ch >= lineObj.text.length) {
      ch = lineObj.text.length;
      sticky = "before";
    } else if (ch <= 0) {
      ch = 0;
      sticky = "after";
    }
    if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }

    function getBidi(ch, partPos, invert) {
      var part = order[partPos], right = part.level == 1;
      return get(invert ? ch - 1 : ch, right != invert)
    }
    var partPos = getBidiPartAt(order, ch, sticky);
    var other = bidiOther;
    var val = getBidi(ch, partPos, sticky == "before");
    if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
    return val
  }

  // Used to cheaply estimate the coordinates for a position. Used for
  // intermediate scroll updates.
  function estimateCoords(cm, pos) {
    var left = 0;
    pos = clipPos(cm.doc, pos);
    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
    var lineObj = getLine(cm.doc, pos.line);
    var top = heightAtLine(lineObj) + paddingTop(cm.display);
    return {left: left, right: left, top: top, bottom: top + lineObj.height}
  }

  // Positions returned by coordsChar contain some extra information.
  // xRel is the relative x position of the input coordinates compared
  // to the found position (so xRel > 0 means the coordinates are to
  // the right of the character position, for example). When outside
  // is true, that means the coordinates lie outside the line's
  // vertical range.
  function PosWithInfo(line, ch, sticky, outside, xRel) {
    var pos = Pos(line, ch, sticky);
    pos.xRel = xRel;
    if (outside) { pos.outside = outside; }
    return pos
  }

  // Compute the character position closest to the given coordinates.
  // Input must be lineSpace-local ("div" coordinate system).
  function coordsChar(cm, x, y) {
    var doc = cm.doc;
    y += cm.display.viewOffset;
    if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
    if (lineN > last)
      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
    if (x < 0) { x = 0; }

    var lineObj = getLine(doc, lineN);
    for (;;) {
      var found = coordsCharInner(cm, lineObj, lineN, x, y);
      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
      if (!collapsed) { return found }
      var rangeEnd = collapsed.find(1);
      if (rangeEnd.line == lineN) { return rangeEnd }
      lineObj = getLine(doc, lineN = rangeEnd.line);
    }
  }

  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
    y -= widgetTopHeight(lineObj);
    var end = lineObj.text.length;
    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
    return {begin: begin, end: end}
  }

  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
  }

  // Returns true if the given side of a box is after the given
  // coordinates, in top-to-bottom, left-to-right order.
  function boxIsAfter(box, x, y, left) {
    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
  }

  function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
    // Move y into line-local coordinate space
    y -= heightAtLine(lineObj);
    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
    // When directly calling `measureCharPrepared`, we have to adjust
    // for the widgets at this line.
    var widgetHeight$$1 = widgetTopHeight(lineObj);
    var begin = 0, end = lineObj.text.length, ltr = true;

    var order = getOrder(lineObj, cm.doc.direction);
    // If the line isn't plain left-to-right text, first figure out
    // which bidi section the coordinates fall into.
    if (order) {
      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
                   (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
      ltr = part.level != 1;
      // The awkward -1 offsets are needed because findFirst (called
      // on these below) will treat its first bound as inclusive,
      // second as exclusive, but we want to actually address the
      // characters in the part's range
      begin = ltr ? part.from : part.to - 1;
      end = ltr ? part.to : part.from - 1;
    }

    // A binary search to find the first character whose bounding box
    // starts after the coordinates. If we run across any whose box wrap
    // the coordinates, store that.
    var chAround = null, boxAround = null;
    var ch = findFirst(function (ch) {
      var box = measureCharPrepared(cm, preparedMeasure, ch);
      box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
      if (!boxIsAfter(box, x, y, false)) { return false }
      if (box.top <= y && box.left <= x) {
        chAround = ch;
        boxAround = box;
      }
      return true
    }, begin, end);

    var baseX, sticky, outside = false;
    // If a box around the coordinates was found, use that
    if (boxAround) {
      // Distinguish coordinates nearer to the left or right side of the box
      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
      ch = chAround + (atStart ? 0 : 1);
      sticky = atStart ? "after" : "before";
      baseX = atLeft ? boxAround.left : boxAround.right;
    } else {
      // (Adjust for extended bound, if necessary.)
      if (!ltr && (ch == end || ch == begin)) { ch++; }
      // To determine which side to associate with, get the box to the
      // left of the character and compare it's vertical position to the
      // coordinates
      sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
        "after" : "before";
      // Now get accurate coordinates for this place, in order to get a
      // base X position
      var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
      baseX = coords.left;
      outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
    }

    ch = skipExtendingChars(lineObj.text, ch, 1);
    return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
  }

  function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
    // Bidi parts are sorted left-to-right, and in a non-line-wrapping
    // situation, we can take this ordering to correspond to the visual
    // ordering. This finds the first part whose end is after the given
    // coordinates.
    var index = findFirst(function (i) {
      var part = order[i], ltr = part.level != 1;
      return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
                                     "line", lineObj, preparedMeasure), x, y, true)
    }, 0, order.length - 1);
    var part = order[index];
    // If this isn't the first part, the part's start is also after
    // the coordinates, and the coordinates aren't on the same line as
    // that start, move one part back.
    if (index > 0) {
      var ltr = part.level != 1;
      var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
                               "line", lineObj, preparedMeasure);
      if (boxIsAfter(start, x, y, true) && start.top > y)
        { part = order[index - 1]; }
    }
    return part
  }

  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
    // In a wrapped line, rtl text on wrapping boundaries can do things
    // that don't correspond to the ordering in our `order` array at
    // all, so a binary search doesn't work, and we want to return a
    // part that only spans one line so that the binary search in
    // coordsCharInner is safe. As such, we first find the extent of the
    // wrapped line, and then do a flat search in which we discard any
    // spans that aren't on the line.
    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
    var begin = ref.begin;
    var end = ref.end;
    if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
    var part = null, closestDist = null;
    for (var i = 0; i < order.length; i++) {
      var p = order[i];
      if (p.from >= end || p.to <= begin) { continue }
      var ltr = p.level != 1;
      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
      // Weigh against spans ending before this, so that they are only
      // picked if nothing ends after
      var dist = endX < x ? x - endX + 1e9 : endX - x;
      if (!part || closestDist > dist) {
        part = p;
        closestDist = dist;
      }
    }
    if (!part) { part = order[order.length - 1]; }
    // Clip the part to the wrapped line.
    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
    return part
  }

  var measureText;
  // Compute the default text height.
  function textHeight(display) {
    if (display.cachedTextHeight != null) { return display.cachedTextHeight }
    if (measureText == null) {
      measureText = elt("pre", null, "CodeMirror-line-like");
      // Measure a bunch of lines, for browsers that compute
      // fractional heights.
      for (var i = 0; i < 49; ++i) {
        measureText.appendChild(document.createTextNode("x"));
        measureText.appendChild(elt("br"));
      }
      measureText.appendChild(document.createTextNode("x"));
    }
    removeChildrenAndAdd(display.measure, measureText);
    var height = measureText.offsetHeight / 50;
    if (height > 3) { display.cachedTextHeight = height; }
    removeChildren(display.measure);
    return height || 1
  }

  // Compute the default character width.
  function charWidth(display) {
    if (display.cachedCharWidth != null) { return display.cachedCharWidth }
    var anchor = elt("span", "xxxxxxxxxx");
    var pre = elt("pre", [anchor], "CodeMirror-line-like");
    removeChildrenAndAdd(display.measure, pre);
    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
    if (width > 2) { display.cachedCharWidth = width; }
    return width || 10
  }

  // Do a bulk-read of the DOM positions and sizes needed to draw the
  // view, so that we don't interleave reading and writing to the DOM.
  function getDimensions(cm) {
    var d = cm.display, left = {}, width = {};
    var gutterLeft = d.gutters.clientLeft;
    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
      var id = cm.display.gutterSpecs[i].className;
      left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
      width[id] = n.clientWidth;
    }
    return {fixedPos: compensateForHScroll(d),
            gutterTotalWidth: d.gutters.offsetWidth,
            gutterLeft: left,
            gutterWidth: width,
            wrapperWidth: d.wrapper.clientWidth}
  }

  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
  // but using getBoundingClientRect to get a sub-pixel-accurate
  // result.
  function compensateForHScroll(display) {
    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
  }

  // Returns a function that estimates the height of a line, to use as
  // first approximation until the line becomes visible (and is thus
  // properly measurable).
  function estimateHeight(cm) {
    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
    return function (line) {
      if (lineIsHidden(cm.doc, line)) { return 0 }

      var widgetsHeight = 0;
      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
      } }

      if (wrapping)
        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
      else
        { return widgetsHeight + th }
    }
  }

  function estimateLineHeights(cm) {
    var doc = cm.doc, est = estimateHeight(cm);
    doc.iter(function (line) {
      var estHeight = est(line);
      if (estHeight != line.height) { updateLineHeight(line, estHeight); }
    });
  }

  // Given a mouse event, find the corresponding position. If liberal
  // is false, it checks whether a gutter or scrollbar was clicked,
  // and returns null if it was. forRect is used by rectangular
  // selections, and tries to estimate a character position even for
  // coordinates beyond the right of the text.
  function posFromMouse(cm, e, liberal, forRect) {
    var display = cm.display;
    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }

    var x, y, space = display.lineSpace.getBoundingClientRect();
    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
    try { x = e.clientX - space.left; y = e.clientY - space.top; }
    catch (e) { return null }
    var coords = coordsChar(cm, x, y), line;
    if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
    }
    return coords
  }

  // Find the view element corresponding to a given line. Return null
  // when the line isn't visible.
  function findViewIndex(cm, n) {
    if (n >= cm.display.viewTo) { return null }
    n -= cm.display.viewFrom;
    if (n < 0) { return null }
    var view = cm.display.view;
    for (var i = 0; i < view.length; i++) {
      n -= view[i].size;
      if (n < 0) { return i }
    }
  }

  // Updates the display.view data structure for a given change to the
  // document. From and to are in pre-change coordinates. Lendiff is
  // the amount of lines added or subtracted by the change. This is
  // used for changes that span multiple lines, or change the way
  // lines are divided into visual lines. regLineChange (below)
  // registers single-line changes.
  function regChange(cm, from, to, lendiff) {
    if (from == null) { from = cm.doc.first; }
    if (to == null) { to = cm.doc.first + cm.doc.size; }
    if (!lendiff) { lendiff = 0; }

    var display = cm.display;
    if (lendiff && to < display.viewTo &&
        (display.updateLineNumbers == null || display.updateLineNumbers > from))
      { display.updateLineNumbers = from; }

    cm.curOp.viewChanged = true;

    if (from >= display.viewTo) { // Change after
      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
        { resetView(cm); }
    } else if (to <= display.viewFrom) { // Change before
      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
        resetView(cm);
      } else {
        display.viewFrom += lendiff;
        display.viewTo += lendiff;
      }
    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
      resetView(cm);
    } else if (from <= display.viewFrom) { // Top overlap
      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
      if (cut) {
        display.view = display.view.slice(cut.index);
        display.viewFrom = cut.lineN;
        display.viewTo += lendiff;
      } else {
        resetView(cm);
      }
    } else if (to >= display.viewTo) { // Bottom overlap
      var cut$1 = viewCuttingPoint(cm, from, from, -1);
      if (cut$1) {
        display.view = display.view.slice(0, cut$1.index);
        display.viewTo = cut$1.lineN;
      } else {
        resetView(cm);
      }
    } else { // Gap in the middle
      var cutTop = viewCuttingPoint(cm, from, from, -1);
      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
      if (cutTop && cutBot) {
        display.view = display.view.slice(0, cutTop.index)
          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
          .concat(display.view.slice(cutBot.index));
        display.viewTo += lendiff;
      } else {
        resetView(cm);
      }
    }

    var ext = display.externalMeasured;
    if (ext) {
      if (to < ext.lineN)
        { ext.lineN += lendiff; }
      else if (from < ext.lineN + ext.size)
        { display.externalMeasured = null; }
    }
  }

  // Register a change to a single line. Type must be one of "text",
  // "gutter", "class", "widget"
  function regLineChange(cm, line, type) {
    cm.curOp.viewChanged = true;
    var display = cm.display, ext = cm.display.externalMeasured;
    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
      { display.externalMeasured = null; }

    if (line < display.viewFrom || line >= display.viewTo) { return }
    var lineView = display.view[findViewIndex(cm, line)];
    if (lineView.node == null) { return }
    var arr = lineView.changes || (lineView.changes = []);
    if (indexOf(arr, type) == -1) { arr.push(type); }
  }

  // Clear the view.
  function resetView(cm) {
    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
    cm.display.view = [];
    cm.display.viewOffset = 0;
  }

  function viewCuttingPoint(cm, oldN, newN, dir) {
    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
      { return {index: index, lineN: newN} }
    var n = cm.display.viewFrom;
    for (var i = 0; i < index; i++)
      { n += view[i].size; }
    if (n != oldN) {
      if (dir > 0) {
        if (index == view.length - 1) { return null }
        diff = (n + view[index].size) - oldN;
        index++;
      } else {
        diff = n - oldN;
      }
      oldN += diff; newN += diff;
    }
    while (visualLineNo(cm.doc, newN) != newN) {
      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
      index += dir;
    }
    return {index: index, lineN: newN}
  }

  // Force the view to cover a given range, adding empty view element
  // or clipping off existing ones as needed.
  function adjustView(cm, from, to) {
    var display = cm.display, view = display.view;
    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
      display.view = buildViewArray(cm, from, to);
      display.viewFrom = from;
    } else {
      if (display.viewFrom > from)
        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
      else if (display.viewFrom < from)
        { display.view = display.view.slice(findViewIndex(cm, from)); }
      display.viewFrom = from;
      if (display.viewTo < to)
        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
      else if (display.viewTo > to)
        { display.view = display.view.slice(0, findViewIndex(cm, to)); }
    }
    display.viewTo = to;
  }

  // Count the number of lines in the view whose DOM representation is
  // out of date (or nonexistent).
  function countDirtyView(cm) {
    var view = cm.display.view, dirty = 0;
    for (var i = 0; i < view.length; i++) {
      var lineView = view[i];
      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
    }
    return dirty
  }

  function updateSelection(cm) {
    cm.display.input.showSelection(cm.display.input.prepareSelection());
  }

  function prepareSelection(cm, primary) {
    if ( primary === void 0 ) primary = true;

    var doc = cm.doc, result = {};
    var curFragment = result.cursors = document.createDocumentFragment();
    var selFragment = result.selection = document.createDocumentFragment();

    for (var i = 0; i < doc.sel.ranges.length; i++) {
      //if (!primary && i == doc.sel.primIndex) { continue }
      var range$$1 = doc.sel.ranges[i];
      if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
      var collapsed = range$$1.empty();
      if (collapsed || cm.options.showCursorWhenSelecting)
        { drawSelectionCursor(cm, range$$1.head, curFragment); }
      if (!collapsed)
        { drawSelectionRange(cm, range$$1, selFragment); }
    }
    return result
  }

  // Draws a cursor for the given range
  function drawSelectionCursor(cm, head, output) {
    var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);

    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
    cursor.style.left = pos.left + "px";
    cursor.style.top = pos.top + "px";
    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";

    if (pos.other) {
      // Secondary cursor, shown when on a 'jump' in bi-directional text
      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
      otherCursor.style.display = "";
      otherCursor.style.left = pos.other.left + "px";
      otherCursor.style.top = pos.other.top + "px";
      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
    }
  }

  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }

  // Draws the given range as a highlighted selection
  function drawSelectionRange(cm, range$$1, output) {
    var display = cm.display, doc = cm.doc;
    var fragment = document.createDocumentFragment();
    var padding = paddingH(cm.display), leftSide = padding.left;
    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
    var docLTR = doc.direction == "ltr";

    function add(left, top, width, bottom) {
      if (top < 0) { top = 0; }
      top = Math.round(top);
      bottom = Math.round(bottom);
      fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n                             top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n                             height: " + (bottom - top) + "px")));
    }

    function drawForLine(line, fromArg, toArg) {
      var lineObj = getLine(doc, line);
      var lineLen = lineObj.text.length;
      var start, end;
      function coords(ch, bias) {
        return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
      }

      function wrapX(pos, dir, side) {
        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
        var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
        var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
        return coords(ch, prop)[prop]
      }

      var order = getOrder(lineObj, doc.direction);
      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
        var ltr = dir == "ltr";
        var fromPos = coords(from, ltr ? "left" : "right");
        var toPos = coords(to - 1, ltr ? "right" : "left");

        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
        var first = i == 0, last = !order || i == order.length - 1;
        if (toPos.top - fromPos.top <= 3) { // Single line
          var openLeft = (docLTR ? openStart : openEnd) && first;
          var openRight = (docLTR ? openEnd : openStart) && last;
          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
          add(left, fromPos.top, right - left, fromPos.bottom);
        } else { // Multiple lines
          var topLeft, topRight, botLeft, botRight;
          if (ltr) {
            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
            topRight = docLTR ? rightSide : wrapX(from, dir, "before");
            botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
            botRight = docLTR && openEnd && last ? rightSide : toPos.right;
          } else {
            topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
            botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
          }
          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
        }

        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
        if (cmpCoords(toPos, start) < 0) { start = toPos; }
        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
        if (cmpCoords(toPos, end) < 0) { end = toPos; }
      });
      return {start: start, end: end}
    }

    var sFrom = range$$1.from(), sTo = range$$1.to();
    if (sFrom.line == sTo.line) {
      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
    } else {
      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
      var singleVLine = visualLine(fromLine) == visualLine(toLine);
      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
      if (singleVLine) {
        if (leftEnd.top < rightStart.top - 2) {
          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
        } else {
          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
        }
      }
      if (leftEnd.bottom < rightStart.top)
        { add(leftSide, leftEnd.bottom, null, rightStart.top); }
    }

    output.appendChild(fragment);
  }

  // Cursor-blinking
  function restartBlink(cm) {
    if (!cm.state.focused) { return }
    var display = cm.display;
    clearInterval(display.blinker);
    var on = true;
    display.cursorDiv.style.visibility = "";
    if (cm.options.cursorBlinkRate > 0)
      { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
        cm.options.cursorBlinkRate); }
    else if (cm.options.cursorBlinkRate < 0)
      { display.cursorDiv.style.visibility = "hidden"; }
  }

  function ensureFocus(cm) {
    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
  }

  function delayBlurEvent(cm) {
    cm.state.delayingBlurEvent = true;
    setTimeout(function () { if (cm.state.delayingBlurEvent) {
      cm.state.delayingBlurEvent = false;
      onBlur(cm);
    } }, 100);
  }

  function onFocus(cm, e) {
    if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }

    if (cm.options.readOnly == "nocursor") { return }
    if (!cm.state.focused) {
      signal(cm, "focus", cm, e);
      cm.state.focused = true;
      addClass(cm.display.wrapper, "CodeMirror-focused");
      // This test prevents this from firing when a context
      // menu is closed (since the input reset would kill the
      // select-all detection hack)
      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
        cm.display.input.reset();
        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
      }
      cm.display.input.receivedFocus();
    }
    restartBlink(cm);
  }
  function onBlur(cm, e) {
    if (cm.state.delayingBlurEvent) { return }

    if (cm.state.focused) {
      signal(cm, "blur", cm, e);
      cm.state.focused = false;
      rmClass(cm.display.wrapper, "CodeMirror-focused");
    }
    clearInterval(cm.display.blinker);
    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
  }

  // Read the actual heights of the rendered lines, and update their
  // stored heights to match.
  function updateHeightsInViewport(cm) {
    var display = cm.display;
    var prevBottom = display.lineDiv.offsetTop;
    for (var i = 0; i < display.view.length; i++) {
      var cur = display.view[i], wrapping = cm.options.lineWrapping;
      var height = (void 0), width = 0;
      if (cur.hidden) { continue }
      if (ie && ie_version < 8) {
        var bot = cur.node.offsetTop + cur.node.offsetHeight;
        height = bot - prevBottom;
        prevBottom = bot;
      } else {
        var box = cur.node.getBoundingClientRect();
        height = box.bottom - box.top;
        // Check that lines don't extend past the right of the current
        // editor width
        if (!wrapping && cur.text.firstChild)
          { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
      }
      var diff = cur.line.height - height;
      if (diff > .005 || diff < -.005) {
        updateLineHeight(cur.line, height);
        updateWidgetHeight(cur.line);
        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
          { updateWidgetHeight(cur.rest[j]); } }
      }
      if (width > cm.display.sizerWidth) {
        var chWidth = Math.ceil(width / charWidth(cm.display));
        if (chWidth > cm.display.maxLineLength) {
          cm.display.maxLineLength = chWidth;
          cm.display.maxLine = cur.line;
          cm.display.maxLineChanged = true;
        }
      }
    }
  }

  // Read and store the height of line widgets associated with the
  // given line.
  function updateWidgetHeight(line) {
    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
      var w = line.widgets[i], parent = w.node.parentNode;
      if (parent) { w.height = parent.offsetHeight; }
    } }
  }

  // Compute the lines that are visible in a given viewport (defaults
  // the the current scroll position). viewport may contain top,
  // height, and ensure (see op.scrollToPos) properties.
  function visibleLines(display, doc, viewport) {
    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
    top = Math.floor(top - paddingTop(display));
    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;

    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
    // forces those lines into the viewport (if possible).
    if (viewport && viewport.ensure) {
      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
      if (ensureFrom < from) {
        from = ensureFrom;
        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
        to = ensureTo;
      }
    }
    return {from: from, to: Math.max(to, from + 1)}
  }

  // SCROLLING THINGS INTO VIEW

  // If an editor sits on the top or bottom of the window, partially
  // scrolled out of view, this ensures that the cursor is visible.
  function maybeScrollWindow(cm, rect) {
    if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }

    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
    if (rect.top + box.top < 0) { doScroll = true; }
    else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
    if (doScroll != null && !phantom) {
      var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n                         top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n                         height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n                         left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"));
      cm.display.lineSpace.appendChild(scrollNode);
      scrollNode.scrollIntoView(doScroll);
      cm.display.lineSpace.removeChild(scrollNode);
    }
  }

  // Scroll a given position into view (immediately), verifying that
  // it actually became visible (as line heights are accurately
  // measured, the position of something may 'drift' during drawing).
  function scrollPosIntoView(cm, pos, end, margin) {
    if (margin == null) { margin = 0; }
    var rect;
    if (!cm.options.lineWrapping && pos == end) {
      // Set pos and end to the cursor positions around the character pos sticks to
      // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
      // If pos == Pos(_, 0, "before"), pos and end are unchanged
      pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
      end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
    }
    for (var limit = 0; limit < 5; limit++) {
      var changed = false;
      var coords = cursorCoords(cm, pos);
      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
      rect = {left: Math.min(coords.left, endCoords.left),
              top: Math.min(coords.top, endCoords.top) - margin,
              right: Math.max(coords.left, endCoords.left),
              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
      var scrollPos = calculateScrollPos(cm, rect);
      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
      if (scrollPos.scrollTop != null) {
        updateScrollTop(cm, scrollPos.scrollTop);
        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
      }
      if (scrollPos.scrollLeft != null) {
        setScrollLeft(cm, scrollPos.scrollLeft);
        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
      }
      if (!changed) { break }
    }
    return rect
  }

  // Scroll a given set of coordinates into view (immediately).
  function scrollIntoView(cm, rect) {
    var scrollPos = calculateScrollPos(cm, rect);
    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
  }

  // Calculate a new scroll position needed to scroll the given
  // rectangle into view. Returns an object with scrollTop and
  // scrollLeft properties. When these are undefined, the
  // vertical/horizontal position does not need to be adjusted.
  function calculateScrollPos(cm, rect) {
    var display = cm.display, snapMargin = textHeight(cm.display);
    if (rect.top < 0) { rect.top = 0; }
    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
    var screen = displayHeight(cm), result = {};
    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
    var docBottom = cm.doc.height + paddingVert(display);
    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
    if (rect.top < screentop) {
      result.scrollTop = atTop ? 0 : rect.top;
    } else if (rect.bottom > screentop + screen) {
      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
      if (newTop != screentop) { result.scrollTop = newTop; }
    }

    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
    var tooWide = rect.right - rect.left > screenw;
    if (tooWide) { rect.right = rect.left + screenw; }
    if (rect.left < 10)
      { result.scrollLeft = 0; }
    else if (rect.left < screenleft)
      { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
    else if (rect.right > screenw + screenleft - 3)
      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
    return result
  }

  // Store a relative adjustment to the scroll position in the current
  // operation (to be applied when the operation finishes).
  function addToScrollTop(cm, top) {
    if (top == null) { return }
    resolveScrollToPos(cm);
    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
  }

  // Make sure that at the end of the operation the current cursor is
  // shown.
  function ensureCursorVisible(cm) {
    resolveScrollToPos(cm);
    var cur = cm.getCursor();
    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
  }

  function scrollToCoords(cm, x, y) {
    if (x != null || y != null) { resolveScrollToPos(cm); }
    if (x != null) { cm.curOp.scrollLeft = x; }
    if (y != null) { cm.curOp.scrollTop = y; }
  }

  function scrollToRange(cm, range$$1) {
    resolveScrollToPos(cm);
    cm.curOp.scrollToPos = range$$1;
  }

  // When an operation has its scrollToPos property set, and another
  // scroll action is applied before the end of the operation, this
  // 'simulates' scrolling that position into view in a cheap way, so
  // that the effect of intermediate scroll commands is not ignored.
  function resolveScrollToPos(cm) {
    var range$$1 = cm.curOp.scrollToPos;
    if (range$$1) {
      cm.curOp.scrollToPos = null;
      var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
      scrollToCoordsRange(cm, from, to, range$$1.margin);
    }
  }

  function scrollToCoordsRange(cm, from, to, margin) {
    var sPos = calculateScrollPos(cm, {
      left: Math.min(from.left, to.left),
      top: Math.min(from.top, to.top) - margin,
      right: Math.max(from.right, to.right),
      bottom: Math.max(from.bottom, to.bottom) + margin
    });
    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
  }

  // Sync the scrollable area and scrollbars, ensure the viewport
  // covers the visible area.
  function updateScrollTop(cm, val) {
    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
    if (!gecko) { updateDisplaySimple(cm, {top: val}); }
    setScrollTop(cm, val, true);
    if (gecko) { updateDisplaySimple(cm); }
    startWorker(cm, 100);
  }

  function setScrollTop(cm, val, forceScroll) {
    val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
    cm.doc.scrollTop = val;
    cm.display.scrollbars.setScrollTop(val);
    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
  }

  // Sync scroller and scrollbar, ensure the gutter elements are
  // aligned.
  function setScrollLeft(cm, val, isScroller, forceScroll) {
    val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
    cm.doc.scrollLeft = val;
    alignHorizontally(cm);
    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
    cm.display.scrollbars.setScrollLeft(val);
  }

  // SCROLLBARS

  // Prepare DOM reads needed to update the scrollbars. Done in one
  // shot to minimize update/measure roundtrips.
  function measureForScrollbars(cm) {
    var d = cm.display, gutterW = d.gutters.offsetWidth;
    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
    return {
      clientHeight: d.scroller.clientHeight,
      viewHeight: d.wrapper.clientHeight,
      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
      viewWidth: d.wrapper.clientWidth,
      barLeft: cm.options.fixedGutter ? gutterW : 0,
      docHeight: docH,
      scrollHeight: docH + scrollGap(cm) + d.barHeight,
      nativeBarWidth: d.nativeBarWidth,
      gutterWidth: gutterW
    }
  }

  var NativeScrollbars = function(place, scroll, cm) {
    this.cm = cm;
    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
    vert.tabIndex = horiz.tabIndex = -1;
    place(vert); place(horiz);

    on(vert, "scroll", function () {
      if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
    });
    on(horiz, "scroll", function () {
      if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
    });

    this.checkedZeroWidth = false;
    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
  };

  NativeScrollbars.prototype.update = function (measure) {
    var needsH = measure.scrollWidth > measure.clientWidth + 1;
    var needsV = measure.scrollHeight > measure.clientHeight + 1;
    var sWidth = measure.nativeBarWidth;

    if (needsV) {
      this.vert.style.display = "block";
      this.vert.style.bottom = needsH ? sWidth + "px" : "0";
      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
      // A bug in IE8 can cause this value to be negative, so guard it.
      this.vert.firstChild.style.height =
        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
    } else {
      this.vert.style.display = "";
      this.vert.firstChild.style.height = "0";
    }

    if (needsH) {
      this.horiz.style.display = "block";
      this.horiz.style.right = needsV ? sWidth + "px" : "0";
      this.horiz.style.left = measure.barLeft + "px";
      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
      this.horiz.firstChild.style.width =
        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
    } else {
      this.horiz.style.display = "";
      this.horiz.firstChild.style.width = "0";
    }

    if (!this.checkedZeroWidth && measure.clientHeight > 0) {
      if (sWidth == 0) { this.zeroWidthHack(); }
      this.checkedZeroWidth = true;
    }

    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
  };

  NativeScrollbars.prototype.setScrollLeft = function (pos) {
    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
  };

  NativeScrollbars.prototype.setScrollTop = function (pos) {
    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
  };

  NativeScrollbars.prototype.zeroWidthHack = function () {
    var w = mac && !mac_geMountainLion ? "12px" : "18px";
    this.horiz.style.height = this.vert.style.width = w;
    this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
    this.disableHoriz = new Delayed;
    this.disableVert = new Delayed;
  };

  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
    bar.style.pointerEvents = "auto";
    function maybeDisable() {
      // To find out whether the scrollbar is still visible, we
      // check whether the element under the pixel in the bottom
      // right corner of the scrollbar box is the scrollbar box
      // itself (when the bar is still visible) or its filler child
      // (when the bar is hidden). If it is still visible, we keep
      // it enabled, if it's hidden, we disable pointer events.
      var box = bar.getBoundingClientRect();
      var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
      if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
      else { delay.set(1000, maybeDisable); }
    }
    delay.set(1000, maybeDisable);
  };

  NativeScrollbars.prototype.clear = function () {
    var parent = this.horiz.parentNode;
    parent.removeChild(this.horiz);
    parent.removeChild(this.vert);
  };

  var NullScrollbars = function () {};

  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
  NullScrollbars.prototype.setScrollLeft = function () {};
  NullScrollbars.prototype.setScrollTop = function () {};
  NullScrollbars.prototype.clear = function () {};

  function updateScrollbars(cm, measure) {
    if (!measure) { measure = measureForScrollbars(cm); }
    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
    updateScrollbarsInner(cm, measure);
    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
        { updateHeightsInViewport(cm); }
      updateScrollbarsInner(cm, measureForScrollbars(cm));
      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
    }
  }

  // Re-synchronize the fake scrollbars with the actual size of the
  // content.
  function updateScrollbarsInner(cm, measure) {
    var d = cm.display;
    var sizes = d.scrollbars.update(measure);

    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
    d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";

    if (sizes.right && sizes.bottom) {
      d.scrollbarFiller.style.display = "block";
      d.scrollbarFiller.style.height = sizes.bottom + "px";
      d.scrollbarFiller.style.width = sizes.right + "px";
    } else { d.scrollbarFiller.style.display = ""; }
    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
      d.gutterFiller.style.display = "block";
      d.gutterFiller.style.height = sizes.bottom + "px";
      d.gutterFiller.style.width = measure.gutterWidth + "px";
    } else { d.gutterFiller.style.display = ""; }
  }

  var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};

  function initScrollbars(cm) {
    if (cm.display.scrollbars) {
      cm.display.scrollbars.clear();
      if (cm.display.scrollbars.addClass)
        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
    }

    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
      // Prevent clicks in the scrollbars from killing focus
      on(node, "mousedown", function () {
        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
      });
      node.setAttribute("cm-not-content", "true");
    }, function (pos, axis) {
      if (axis == "horizontal") { setScrollLeft(cm, pos); }
      else { updateScrollTop(cm, pos); }
    }, cm);
    if (cm.display.scrollbars.addClass)
      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
  }

  // Operations are used to wrap a series of changes to the editor
  // state in such a way that each change won't have to update the
  // cursor and display (which would be awkward, slow, and
  // error-prone). Instead, display updates are batched and then all
  // combined and executed at once.

  var nextOpId = 0;
  // Start a new operation.
  function startOperation(cm) {
    cm.curOp = {
      cm: cm,
      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
      startHeight: cm.doc.height, // Used to detect need to update scrollbar
      forceUpdate: false,      // Used to force a redraw
      updateInput: 0,       // Whether to reset the input textarea
      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
      changeObjs: null,        // Accumulated changes, for firing change events
      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
      selectionChanged: false, // Whether the selection needs to be redrawn
      updateMaxLine: false,    // Set when the widest line needs to be determined anew
      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
      scrollToPos: null,       // Used to scroll to a specific position
      focus: false,
      id: ++nextOpId           // Unique ID
    };
    pushOperation(cm.curOp);
  }

  // Finish an operation, updating the display and signalling delayed events
  function endOperation(cm) {
    var op = cm.curOp;
    if (op) { finishOperation(op, function (group) {
      for (var i = 0; i < group.ops.length; i++)
        { group.ops[i].cm.curOp = null; }
      endOperations(group);
    }); }
  }

  // The DOM updates done when an operation finishes are batched so
  // that the minimum number of relayouts are required.
  function endOperations(group) {
    var ops = group.ops;
    for (var i = 0; i < ops.length; i++) // Read DOM
      { endOperation_R1(ops[i]); }
    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
      { endOperation_W1(ops[i$1]); }
    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
      { endOperation_R2(ops[i$2]); }
    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
      { endOperation_W2(ops[i$3]); }
    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
      { endOperation_finish(ops[i$4]); }
  }

  function endOperation_R1(op) {
    var cm = op.cm, display = cm.display;
    maybeClipScrollbars(cm);
    if (op.updateMaxLine) { findMaxLine(cm); }

    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
                         op.scrollToPos.to.line >= display.viewTo) ||
      display.maxLineChanged && cm.options.lineWrapping;
    op.update = op.mustUpdate &&
      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
  }

  function endOperation_W1(op) {
    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
  }

  function endOperation_R2(op) {
    var cm = op.cm, display = cm.display;
    if (op.updatedDisplay) { updateHeightsInViewport(cm); }

    op.barMeasure = measureForScrollbars(cm);

    // If the max line changed since it was last measured, measure it,
    // and ensure the document's width matches it.
    // updateDisplay_W2 will use these properties to do the actual resizing
    if (display.maxLineChanged && !cm.options.lineWrapping) {
      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
      cm.display.sizerWidth = op.adjustWidthTo;
      op.barMeasure.scrollWidth =
        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
    }

    if (op.updatedDisplay || op.selectionChanged)
      { op.preparedSelection = display.input.prepareSelection(); }
  }

  function endOperation_W2(op) {
    var cm = op.cm;

    if (op.adjustWidthTo != null) {
      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
      if (op.maxScrollLeft < cm.doc.scrollLeft)
        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
      cm.display.maxLineChanged = false;
    }

    var takeFocus = op.focus && op.focus == activeElt();
    if (op.preparedSelection)
      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
    if (op.updatedDisplay || op.startHeight != cm.doc.height)
      { updateScrollbars(cm, op.barMeasure); }
    if (op.updatedDisplay)
      { setDocumentHeight(cm, op.barMeasure); }

    if (op.selectionChanged) { restartBlink(cm); }

    if (cm.state.focused && op.updateInput)
      { cm.display.input.reset(op.typing); }
    if (takeFocus) { ensureFocus(op.cm); }
  }

  function endOperation_finish(op) {
    var cm = op.cm, display = cm.display, doc = cm.doc;

    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }

    // Abort mouse wheel delta measurement, when scrolling explicitly
    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
      { display.wheelStartX = display.wheelStartY = null; }

    // Propagate the scroll position to the actual DOM scroller
    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }

    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
    // If we need to scroll a specific position into view, do so.
    if (op.scrollToPos) {
      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
      maybeScrollWindow(cm, rect);
    }

    // Fire events for markers that are hidden/unidden by editing or
    // undoing
    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
    if (hidden) { for (var i = 0; i < hidden.length; ++i)
      { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }

    if (display.wrapper.offsetHeight)
      { doc.scrollTop = cm.display.scroller.scrollTop; }

    // Fire change events, and delayed event handlers
    if (op.changeObjs)
      { signal(cm, "changes", cm, op.changeObjs); }
    if (op.update)
      { op.update.finish(); }
  }

  // Run the given function in an operation
  function runInOp(cm, f) {
    if (cm.curOp) { return f() }
    startOperation(cm);
    try { return f() }
    finally { endOperation(cm); }
  }
  // Wraps a function in an operation. Returns the wrapped function.
  function operation(cm, f) {
    return function() {
      if (cm.curOp) { return f.apply(cm, arguments) }
      startOperation(cm);
      try { return f.apply(cm, arguments) }
      finally { endOperation(cm); }
    }
  }
  // Used to add methods to editor and doc instances, wrapping them in
  // operations.
  function methodOp(f) {
    return function() {
      if (this.curOp) { return f.apply(this, arguments) }
      startOperation(this);
      try { return f.apply(this, arguments) }
      finally { endOperation(this); }
    }
  }
  function docMethodOp(f) {
    return function() {
      var cm = this.cm;
      if (!cm || cm.curOp) { return f.apply(this, arguments) }
      startOperation(cm);
      try { return f.apply(this, arguments) }
      finally { endOperation(cm); }
    }
  }

  // HIGHLIGHT WORKER

  function startWorker(cm, time) {
    if (cm.doc.highlightFrontier < cm.display.viewTo)
      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
  }

  function highlightWorker(cm) {
    var doc = cm.doc;
    if (doc.highlightFrontier >= cm.display.viewTo) { return }
    var end = +new Date + cm.options.workTime;
    var context = getContextBefore(cm, doc.highlightFrontier);
    var changedLines = [];

    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
      if (context.line >= cm.display.viewFrom) { // Visible
        var oldStyles = line.styles;
        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
        var highlighted = highlightLine(cm, line, context, true);
        if (resetState) { context.state = resetState; }
        line.styles = highlighted.styles;
        var oldCls = line.styleClasses, newCls = highlighted.classes;
        if (newCls) { line.styleClasses = newCls; }
        else if (oldCls) { line.styleClasses = null; }
        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
        if (ischange) { changedLines.push(context.line); }
        line.stateAfter = context.save();
        context.nextLine();
      } else {
        if (line.text.length <= cm.options.maxHighlightLength)
          { processLine(cm, line.text, context); }
        line.stateAfter = context.line % 5 == 0 ? context.save() : null;
        context.nextLine();
      }
      if (+new Date > end) {
        startWorker(cm, cm.options.workDelay);
        return true
      }
    });
    doc.highlightFrontier = context.line;
    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
    if (changedLines.length) { runInOp(cm, function () {
      for (var i = 0; i < changedLines.length; i++)
        { regLineChange(cm, changedLines[i], "text"); }
    }); }
  }

  // DISPLAY DRAWING

  var DisplayUpdate = function(cm, viewport, force) {
    var display = cm.display;

    this.viewport = viewport;
    // Store some values that we'll need later (but don't want to force a relayout for)
    this.visible = visibleLines(display, cm.doc, viewport);
    this.editorIsHidden = !display.wrapper.offsetWidth;
    this.wrapperHeight = display.wrapper.clientHeight;
    this.wrapperWidth = display.wrapper.clientWidth;
    this.oldDisplayWidth = displayWidth(cm);
    this.force = force;
    this.dims = getDimensions(cm);
    this.events = [];
  };

  DisplayUpdate.prototype.signal = function (emitter, type) {
    if (hasHandler(emitter, type))
      { this.events.push(arguments); }
  };
  DisplayUpdate.prototype.finish = function () {
      var this$1 = this;

    for (var i = 0; i < this.events.length; i++)
      { signal.apply(null, this$1.events[i]); }
  };

  function maybeClipScrollbars(cm) {
    var display = cm.display;
    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
      display.heightForcer.style.height = scrollGap(cm) + "px";
      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
      display.scrollbarsClipped = true;
    }
  }

  function selectionSnapshot(cm) {
    if (cm.hasFocus()) { return null }
    var active = activeElt();
    if (!active || !contains(cm.display.lineDiv, active)) { return null }
    var result = {activeElt: active};
    if (window.getSelection) {
      var sel = window.getSelection();
      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
        result.anchorNode = sel.anchorNode;
        result.anchorOffset = sel.anchorOffset;
        result.focusNode = sel.focusNode;
        result.focusOffset = sel.focusOffset;
      }
    }
    return result
  }

  function restoreSelection(snapshot) {
    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
    snapshot.activeElt.focus();
    if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
      var sel = window.getSelection(), range$$1 = document.createRange();
      range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
      range$$1.collapse(false);
      sel.removeAllRanges();
      sel.addRange(range$$1);
      sel.extend(snapshot.focusNode, snapshot.focusOffset);
    }
  }

  // Does the actual updating of the line display. Bails out
  // (returning false) when there is nothing to be done and forced is
  // false.
  function updateDisplayIfNeeded(cm, update) {
    var display = cm.display, doc = cm.doc;

    if (update.editorIsHidden) {
      resetView(cm);
      return false
    }

    // Bail out if the visible area is already rendered and nothing changed.
    if (!update.force &&
        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
        display.renderedView == display.view && countDirtyView(cm) == 0)
      { return false }

    if (maybeUpdateLineNumberWidth(cm)) {
      resetView(cm);
      update.dims = getDimensions(cm);
    }

    // Compute a suitable new viewport (from & to)
    var end = doc.first + doc.size;
    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
    if (sawCollapsedSpans) {
      from = visualLineNo(cm.doc, from);
      to = visualLineEndNo(cm.doc, to);
    }

    var different = from != display.viewFrom || to != display.viewTo ||
      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
    adjustView(cm, from, to);

    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
    // Position the mover div to align with the current scroll position
    cm.display.mover.style.top = display.viewOffset + "px";

    var toUpdate = countDirtyView(cm);
    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
      { return false }

    // For big changes, we hide the enclosing element during the
    // update, since that speeds up the operations on most browsers.
    var selSnapshot = selectionSnapshot(cm);
    if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
    patchDisplay(cm, display.updateLineNumbers, update.dims);
    if (toUpdate > 4) { display.lineDiv.style.display = ""; }
    display.renderedView = display.view;
    // There might have been a widget with a focused element that got
    // hidden or updated, if so re-focus it.
    restoreSelection(selSnapshot);

    // Prevent selection and cursors from interfering with the scroll
    // width and height.
    removeChildren(display.cursorDiv);
    removeChildren(display.selectionDiv);
    display.gutters.style.height = display.sizer.style.minHeight = 0;

    if (different) {
      display.lastWrapHeight = update.wrapperHeight;
      display.lastWrapWidth = update.wrapperWidth;
      startWorker(cm, 400);
    }

    display.updateLineNumbers = null;

    return true
  }

  function postUpdateDisplay(cm, update) {
    var viewport = update.viewport;

    for (var first = true;; first = false) {
      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
        // Clip forced viewport to actual scrollable area.
        if (viewport && viewport.top != null)
          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
        // Updated line heights might result in the drawn area not
        // actually covering the viewport. Keep looping until it does.
        update.visible = visibleLines(cm.display, cm.doc, viewport);
        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
          { break }
      }
      if (!updateDisplayIfNeeded(cm, update)) { break }
      updateHeightsInViewport(cm);
      var barMeasure = measureForScrollbars(cm);
      updateSelection(cm);
      updateScrollbars(cm, barMeasure);
      setDocumentHeight(cm, barMeasure);
      update.force = false;
    }

    update.signal(cm, "update", cm);
    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
    }
  }

  function updateDisplaySimple(cm, viewport) {
    var update = new DisplayUpdate(cm, viewport);
    if (updateDisplayIfNeeded(cm, update)) {
      updateHeightsInViewport(cm);
      postUpdateDisplay(cm, update);
      var barMeasure = measureForScrollbars(cm);
      updateSelection(cm);
      updateScrollbars(cm, barMeasure);
      setDocumentHeight(cm, barMeasure);
      update.finish();
    }
  }

  // Sync the actual display DOM structure with display.view, removing
  // nodes for lines that are no longer in view, and creating the ones
  // that are not there yet, and updating the ones that are out of
  // date.
  function patchDisplay(cm, updateNumbersFrom, dims) {
    var display = cm.display, lineNumbers = cm.options.lineNumbers;
    var container = display.lineDiv, cur = container.firstChild;

    function rm(node) {
      var next = node.nextSibling;
      // Works around a throw-scroll bug in OS X Webkit
      if (webkit && mac && cm.display.currentWheelTarget == node)
        { node.style.display = "none"; }
      else
        { node.parentNode.removeChild(node); }
      return next
    }

    var view = display.view, lineN = display.viewFrom;
    // Loop over the elements in the view, syncing cur (the DOM nodes
    // in display.lineDiv) with the view as we go.
    for (var i = 0; i < view.length; i++) {
      var lineView = view[i];
      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
        var node = buildLineElement(cm, lineView, lineN, dims);
        container.insertBefore(node, cur);
      } else { // Already drawn
        while (cur != lineView.node) { cur = rm(cur); }
        var updateNumber = lineNumbers && updateNumbersFrom != null &&
          updateNumbersFrom <= lineN && lineView.lineNumber;
        if (lineView.changes) {
          if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
          updateLineForChanges(cm, lineView, lineN, dims);
        }
        if (updateNumber) {
          removeChildren(lineView.lineNumber);
          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
        }
        cur = lineView.node.nextSibling;
      }
      lineN += lineView.size;
    }
    while (cur) { cur = rm(cur); }
  }

  function updateGutterSpace(display) {
    var width = display.gutters.offsetWidth;
    display.sizer.style.marginLeft = width + "px";
  }

  function setDocumentHeight(cm, measure) {
    cm.display.sizer.style.minHeight = measure.docHeight + "px";
    cm.display.heightForcer.style.top = measure.docHeight + "px";
    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
  }

  // Re-align line numbers and gutter marks to compensate for
  // horizontal scrolling.
  function alignHorizontally(cm) {
    var display = cm.display, view = display.view;
    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
    var gutterW = display.gutters.offsetWidth, left = comp + "px";
    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
      if (cm.options.fixedGutter) {
        if (view[i].gutter)
          { view[i].gutter.style.left = left; }
        if (view[i].gutterBackground)
          { view[i].gutterBackground.style.left = left; }
      }
      var align = view[i].alignable;
      if (align) { for (var j = 0; j < align.length; j++)
        { align[j].style.left = left; } }
    } }
    if (cm.options.fixedGutter)
      { display.gutters.style.left = (comp + gutterW) + "px"; }
  }

  // Used to ensure that the line number gutter is still the right
  // size for the current document size. Returns true when an update
  // is needed.
  function maybeUpdateLineNumberWidth(cm) {
    if (!cm.options.lineNumbers) { return false }
    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
    if (last.length != display.lineNumChars) {
      var test = display.measure.appendChild(elt("div", [elt("div", last)],
                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
      display.lineGutter.style.width = "";
      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
      display.lineNumWidth = display.lineNumInnerWidth + padding;
      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
      display.lineGutter.style.width = display.lineNumWidth + "px";
      updateGutterSpace(cm.display);
      return true
    }
    return false
  }

  function getGutters(gutters, lineNumbers) {
    var result = [], sawLineNumbers = false;
    for (var i = 0; i < gutters.length; i++) {
      var name = gutters[i], style = null;
      if (typeof name != "string") { style = name.style; name = name.className; }
      if (name == "CodeMirror-linenumbers") {
        if (!lineNumbers) { continue }
        else { sawLineNumbers = true; }
      }
      result.push({className: name, style: style});
    }
    if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
    return result
  }

  // Rebuild the gutter elements, ensure the margin to the left of the
  // code matches their width.
  function renderGutters(display) {
    var gutters = display.gutters, specs = display.gutterSpecs;
    removeChildren(gutters);
    display.lineGutter = null;
    for (var i = 0; i < specs.length; ++i) {
      var ref = specs[i];
      var className = ref.className;
      var style = ref.style;
      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
      if (style) { gElt.style.cssText = style; }
      if (className == "CodeMirror-linenumbers") {
        display.lineGutter = gElt;
        gElt.style.width = (display.lineNumWidth || 1) + "px";
      }
    }
    gutters.style.display = specs.length ? "" : "none";
    updateGutterSpace(display);
  }

  function updateGutters(cm) {
    renderGutters(cm.display);
    regChange(cm);
    alignHorizontally(cm);
  }

  // The display handles the DOM integration, both for input reading
  // and content drawing. It holds references to DOM nodes and
  // display-related state.

  function Display(place, doc, input, options) {
    var d = this;
    this.input = input;

    // Covers bottom-right square when both scrollbars are present.
    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
    d.scrollbarFiller.setAttribute("cm-not-content", "true");
    // Covers bottom of gutter when coverGutterNextToScrollbar is on
    // and h scrollbar is present.
    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
    d.gutterFiller.setAttribute("cm-not-content", "true");
    // Will contain the actual code, positioned to cover the viewport.
    d.lineDiv = eltP("div", null, "CodeMirror-code");
    // Elements are added to these to represent selection and cursors.
    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
    // A visibility: hidden element used to find the size of things.
    d.measure = elt("div", null, "CodeMirror-measure");
    // When lines outside of the viewport are measured, they are drawn in this.
    d.lineMeasure = elt("div", null, "CodeMirror-measure");
    // Wraps everything that needs to exist inside the vertically-padded coordinate system
    d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
                      null, "position: relative; outline: none");
    var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
    // Moved around its parent to cover visible view.
    d.mover = elt("div", [lines], null, "position: relative");
    // Set to the height of the document, allowing scrolling.
    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
    d.sizerWidth = null;
    // Behavior of elts with overflow: auto and padding is
    // inconsistent across browsers. This is used to ensure the
    // scrollable area is big enough.
    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
    // Will contain the gutters, if any.
    d.gutters = elt("div", null, "CodeMirror-gutters");
    d.lineGutter = null;
    // Actual scrollable element.
    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
    d.scroller.setAttribute("tabIndex", "-1");
    // The element in which the editor lives.
    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");

    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }

    if (place) {
      if (place.appendChild) { place.appendChild(d.wrapper); }
      else { place(d.wrapper); }
    }

    // Current rendered range (may be bigger than the view window).
    d.viewFrom = d.viewTo = doc.first;
    d.reportedViewFrom = d.reportedViewTo = doc.first;
    // Information about the rendered lines.
    d.view = [];
    d.renderedView = null;
    // Holds info about a single rendered line when it was rendered
    // for measurement, while not in view.
    d.externalMeasured = null;
    // Empty space (in pixels) above the view
    d.viewOffset = 0;
    d.lastWrapHeight = d.lastWrapWidth = 0;
    d.updateLineNumbers = null;

    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
    d.scrollbarsClipped = false;

    // Used to only resize the line number gutter when necessary (when
    // the amount of lines crosses a boundary that makes its width change)
    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
    // Set to true when a non-horizontal-scrolling line widget is
    // added. As an optimization, line widget aligning is skipped when
    // this is false.
    d.alignWidgets = false;

    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;

    // Tracks the maximum line length so that the horizontal scrollbar
    // can be kept static when scrolling.
    d.maxLine = null;
    d.maxLineLength = 0;
    d.maxLineChanged = false;

    // Used for measuring wheel scrolling granularity
    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;

    // True when shift is held down.
    d.shift = false;

    // Used to track whether anything happened since the context menu
    // was opened.
    d.selForContextMenu = null;

    d.activeTouch = null;

    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
    renderGutters(d);

    input.init(d);
  }

  // Since the delta values reported on mouse wheel events are
  // unstandardized between browsers and even browser versions, and
  // generally horribly unpredictable, this code starts by measuring
  // the scroll effect that the first few mouse wheel events have,
  // and, from that, detects the way it can convert deltas to pixel
  // offsets afterwards.
  //
  // The reason we want to know the amount a wheel event will scroll
  // is that it gives us a chance to update the display before the
  // actual scrolling happens, reducing flickering.

  var wheelSamples = 0, wheelPixelsPerUnit = null;
  // Fill in a browser-detected starting value on browsers where we
  // know one. These don't have to be accurate -- the result of them
  // being wrong would just be a slight flicker on the first wheel
  // scroll (if it is large enough).
  if (ie) { wheelPixelsPerUnit = -.53; }
  else if (gecko) { wheelPixelsPerUnit = 15; }
  else if (chrome) { wheelPixelsPerUnit = -.7; }
  else if (safari) { wheelPixelsPerUnit = -1/3; }

  function wheelEventDelta(e) {
    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
    else if (dy == null) { dy = e.wheelDelta; }
    return {x: dx, y: dy}
  }
  function wheelEventPixels(e) {
    var delta = wheelEventDelta(e);
    delta.x *= wheelPixelsPerUnit;
    delta.y *= wheelPixelsPerUnit;
    return delta
  }

  function onScrollWheel(cm, e) {
    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;

    var display = cm.display, scroll = display.scroller;
    // Quit if there's nothing to scroll here
    var canScrollX = scroll.scrollWidth > scroll.clientWidth;
    var canScrollY = scroll.scrollHeight > scroll.clientHeight;
    if (!(dx && canScrollX || dy && canScrollY)) { return }

    // Webkit browsers on OS X abort momentum scrolls when the target
    // of the scroll event is removed from the scrollable element.
    // This hack (see related code in patchDisplay) makes sure the
    // element is kept around.
    if (dy && mac && webkit) {
      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
        for (var i = 0; i < view.length; i++) {
          if (view[i].node == cur) {
            cm.display.currentWheelTarget = cur;
            break outer
          }
        }
      }
    }

    // On some browsers, horizontal scrolling will cause redraws to
    // happen before the gutter has been realigned, causing it to
    // wriggle around in a most unseemly way. When we have an
    // estimated pixels/delta value, we just handle horizontal
    // scrolling entirely here. It'll be slightly off from native, but
    // better than glitching out.
    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
      if (dy && canScrollY)
        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
      // Only prevent default scrolling if vertical scrolling is
      // actually possible. Otherwise, it causes vertical scroll
      // jitter on OSX trackpads when deltaX is small and deltaY
      // is large (issue #3579)
      if (!dy || (dy && canScrollY))
        { e_preventDefault(e); }
      display.wheelStartX = null; // Abort measurement, if in progress
      return
    }

    // 'Project' the visible viewport to cover the area that is being
    // scrolled into view (if we know enough to estimate it).
    if (dy && wheelPixelsPerUnit != null) {
      var pixels = dy * wheelPixelsPerUnit;
      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
      updateDisplaySimple(cm, {top: top, bottom: bot});
    }

    if (wheelSamples < 20) {
      if (display.wheelStartX == null) {
        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
        display.wheelDX = dx; display.wheelDY = dy;
        setTimeout(function () {
          if (display.wheelStartX == null) { return }
          var movedX = scroll.scrollLeft - display.wheelStartX;
          var movedY = scroll.scrollTop - display.wheelStartY;
          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
            (movedX && display.wheelDX && movedX / display.wheelDX);
          display.wheelStartX = display.wheelStartY = null;
          if (!sample) { return }
          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
          ++wheelSamples;
        }, 200);
      } else {
        display.wheelDX += dx; display.wheelDY += dy;
      }
    }
  }

  // Selection objects are immutable. A new one is created every time
  // the selection changes. A selection is one or more non-overlapping
  // (and non-touching) ranges, sorted, and an integer that indicates
  // which one is the primary selection (the one that's scrolled into
  // view, that getCursor returns, etc).
  var Selection = function(ranges, primIndex) {
    this.ranges = ranges;
    this.primIndex = primIndex;
  };

  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };

  Selection.prototype.equals = function (other) {
      var this$1 = this;

    if (other == this) { return true }
    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
    for (var i = 0; i < this.ranges.length; i++) {
      var here = this$1.ranges[i], there = other.ranges[i];
      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
    }
    return true
  };

  Selection.prototype.deepCopy = function () {
      var this$1 = this;

    var out = [];
    for (var i = 0; i < this.ranges.length; i++)
      { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
    return new Selection(out, this.primIndex)
  };

  Selection.prototype.somethingSelected = function () {
      var this$1 = this;

    for (var i = 0; i < this.ranges.length; i++)
      { if (!this$1.ranges[i].empty()) { return true } }
    return false
  };

  Selection.prototype.contains = function (pos, end) {
      var this$1 = this;

    if (!end) { end = pos; }
    for (var i = 0; i < this.ranges.length; i++) {
      var range = this$1.ranges[i];
      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
        { return i }
    }
    return -1
  };

  var Range = function(anchor, head) {
    this.anchor = anchor; this.head = head;
  };

  Range.prototype.from = function () { return minPos(this.anchor, this.head) };
  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };

  // Take an unsorted, potentially overlapping set of ranges, and
  // build a selection out of it. 'Consumes' ranges array (modifying
  // it).
  function normalizeSelection(cm, ranges, primIndex) {
    var mayTouch = cm && cm.options.selectionsMayTouch;
    var prim = ranges[primIndex];
    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
    primIndex = indexOf(ranges, prim);
    for (var i = 1; i < ranges.length; i++) {
      var cur = ranges[i], prev = ranges[i - 1];
      var diff = cmp(prev.to(), cur.from());
      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
        if (i <= primIndex) { --primIndex; }
        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
      }
    }
    return new Selection(ranges, primIndex)
  }

  function simpleSelection(anchor, head) {
    return new Selection([new Range(anchor, head || anchor)], 0)
  }

  // Compute the position of the end of a change (its 'to' property
  // refers to the pre-change end).
  function changeEnd(change) {
    if (!change.text) { return change.to }
    return Pos(change.from.line + change.text.length - 1,
               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
  }

  // Adjust a position to refer to the post-change position of the
  // same text, or the end of the change if the change covers it.
  function adjustForChange(pos, change) {
    if (cmp(pos, change.from) < 0) { return pos }
    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }

    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
    return Pos(line, ch)
  }

  function computeSelAfterChange(doc, change) {
    var out = [];
    for (var i = 0; i < doc.sel.ranges.length; i++) {
      var range = doc.sel.ranges[i];
      out.push(new Range(adjustForChange(range.anchor, change),
                         adjustForChange(range.head, change)));
    }
    return normalizeSelection(doc.cm, out, doc.sel.primIndex)
  }

  function offsetPos(pos, old, nw) {
    if (pos.line == old.line)
      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
    else
      { return Pos(nw.line + (pos.line - old.line), pos.ch) }
  }

  // Used by replaceSelections to allow moving the selection to the
  // start or around the replaced test. Hint may be "start" or "around".
  function computeReplacedSel(doc, changes, hint) {
    var out = [];
    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
    for (var i = 0; i < changes.length; i++) {
      var change = changes[i];
      var from = offsetPos(change.from, oldPrev, newPrev);
      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
      oldPrev = change.to;
      newPrev = to;
      if (hint == "around") {
        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
        out[i] = new Range(inv ? to : from, inv ? from : to);
      } else {
        out[i] = new Range(from, from);
      }
    }
    return new Selection(out, doc.sel.primIndex)
  }

  // Used to get the editor into a consistent state again when options change.

  function loadMode(cm) {
    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
    resetModeState(cm);
  }

  function resetModeState(cm) {
    cm.doc.iter(function (line) {
      if (line.stateAfter) { line.stateAfter = null; }
      if (line.styles) { line.styles = null; }
    });
    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
    startWorker(cm, 100);
    cm.state.modeGen++;
    if (cm.curOp) { regChange(cm); }
  }

  // DOCUMENT DATA STRUCTURE

  // By default, updates that start and end at the beginning of a line
  // are treated specially, in order to make the association of line
  // widgets and marker elements with the text behave more intuitive.
  function isWholeLineUpdate(doc, change) {
    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
  }

  // Perform a change on the document data structure.
  function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
    function spansFor(n) {return markedSpans ? markedSpans[n] : null}
    function update(line, text, spans) {
      updateLine(line, text, spans, estimateHeight$$1);
      signalLater(line, "change", line, change);
    }
    function linesFor(start, end) {
      var result = [];
      for (var i = start; i < end; ++i)
        { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
      return result
    }

    var from = change.from, to = change.to, text = change.text;
    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;

    // Adjust the line structure
    if (change.full) {
      doc.insert(0, linesFor(0, text.length));
      doc.remove(text.length, doc.size - text.length);
    } else if (isWholeLineUpdate(doc, change)) {
      // This is a whole-line replace. Treated specially to make
      // sure line objects move the way they are supposed to.
      var added = linesFor(0, text.length - 1);
      update(lastLine, lastLine.text, lastSpans);
      if (nlines) { doc.remove(from.line, nlines); }
      if (added.length) { doc.insert(from.line, added); }
    } else if (firstLine == lastLine) {
      if (text.length == 1) {
        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
      } else {
        var added$1 = linesFor(1, text.length - 1);
        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
        doc.insert(from.line + 1, added$1);
      }
    } else if (text.length == 1) {
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
      doc.remove(from.line + 1, nlines);
    } else {
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
      var added$2 = linesFor(1, text.length - 1);
      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
      doc.insert(from.line + 1, added$2);
    }

    signalLater(doc, "change", doc, change);
  }

  // Call f for all linked documents.
  function linkedDocs(doc, f, sharedHistOnly) {
    function propagate(doc, skip, sharedHist) {
      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
        var rel = doc.linked[i];
        if (rel.doc == skip) { continue }
        var shared = sharedHist && rel.sharedHist;
        if (sharedHistOnly && !shared) { continue }
        f(rel.doc, shared);
        propagate(rel.doc, doc, shared);
      } }
    }
    propagate(doc, null, true);
  }

  // Attach a document to an editor.
  function attachDoc(cm, doc) {
    if (doc.cm) { throw new Error("This document is already in use.") }
    cm.doc = doc;
    doc.cm = cm;
    estimateLineHeights(cm);
    loadMode(cm);
    setDirectionClass(cm);
    if (!cm.options.lineWrapping) { findMaxLine(cm); }
    cm.options.mode = doc.modeOption;
    regChange(cm);
  }

  function setDirectionClass(cm) {
  (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
  }

  function directionChanged(cm) {
    runInOp(cm, function () {
      setDirectionClass(cm);
      regChange(cm);
    });
  }

  function History(startGen) {
    // Arrays of change events and selections. Doing something adds an
    // event to done and clears undo. Undoing moves events from done
    // to undone, redoing moves them in the other direction.
    this.done = []; this.undone = [];
    this.undoDepth = Infinity;
    // Used to track when changes can be merged into a single undo
    // event
    this.lastModTime = this.lastSelTime = 0;
    this.lastOp = this.lastSelOp = null;
    this.lastOrigin = this.lastSelOrigin = null;
    // Used by the isClean() method
    this.generation = this.maxGeneration = startGen || 1;
  }

  // Create a history change event from an updateDoc-style change
  // object.
  function historyChangeFromChange(doc, change) {
    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
    return histChange
  }

  // Pop all selection events off the end of a history array. Stop at
  // a change event.
  function clearSelectionEvents(array) {
    while (array.length) {
      var last = lst(array);
      if (last.ranges) { array.pop(); }
      else { break }
    }
  }

  // Find the top change event in the history. Pop off selection
  // events that are in the way.
  function lastChangeEvent(hist, force) {
    if (force) {
      clearSelectionEvents(hist.done);
      return lst(hist.done)
    } else if (hist.done.length && !lst(hist.done).ranges) {
      return lst(hist.done)
    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
      hist.done.pop();
      return lst(hist.done)
    }
  }

  // Register a change in the history. Merges changes that are within
  // a single operation, or are close together with an origin that
  // allows merging (starting with "+") into a single event.
  function addChangeToHistory(doc, change, selAfter, opId) {
    var hist = doc.history;
    hist.undone.length = 0;
    var time = +new Date, cur;
    var last;

    if ((hist.lastOp == opId ||
         hist.lastOrigin == change.origin && change.origin &&
         ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
          change.origin.charAt(0) == "*")) &&
        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
      // Merge this change into the last event
      last = lst(cur.changes);
      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
        // Optimized case for simple insertion -- don't want to add
        // new changesets for every character typed
        last.to = changeEnd(change);
      } else {
        // Add new sub-event
        cur.changes.push(historyChangeFromChange(doc, change));
      }
    } else {
      // Can not be merged, start a new event.
      var before = lst(hist.done);
      if (!before || !before.ranges)
        { pushSelectionToHistory(doc.sel, hist.done); }
      cur = {changes: [historyChangeFromChange(doc, change)],
             generation: hist.generation};
      hist.done.push(cur);
      while (hist.done.length > hist.undoDepth) {
        hist.done.shift();
        if (!hist.done[0].ranges) { hist.done.shift(); }
      }
    }
    hist.done.push(selAfter);
    hist.generation = ++hist.maxGeneration;
    hist.lastModTime = hist.lastSelTime = time;
    hist.lastOp = hist.lastSelOp = opId;
    hist.lastOrigin = hist.lastSelOrigin = change.origin;

    if (!last) { signal(doc, "historyAdded"); }
  }

  function selectionEventCanBeMerged(doc, origin, prev, sel) {
    var ch = origin.charAt(0);
    return ch == "*" ||
      ch == "+" &&
      prev.ranges.length == sel.ranges.length &&
      prev.somethingSelected() == sel.somethingSelected() &&
      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
  }

  // Called whenever the selection changes, sets the new selection as
  // the pending selection in the history, and pushes the old pending
  // selection into the 'done' array when it was significantly
  // different (in number of selected ranges, emptiness, or time).
  function addSelectionToHistory(doc, sel, opId, options) {
    var hist = doc.history, origin = options && options.origin;

    // A new event is started when the previous origin does not match
    // the current, or the origins don't allow matching. Origins
    // starting with * are always merged, those starting with + are
    // merged when similar and close together in time.
    if (opId == hist.lastSelOp ||
        (origin && hist.lastSelOrigin == origin &&
         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
      { hist.done[hist.done.length - 1] = sel; }
    else
      { pushSelectionToHistory(sel, hist.done); }

    hist.lastSelTime = +new Date;
    hist.lastSelOrigin = origin;
    hist.lastSelOp = opId;
    if (options && options.clearRedo !== false)
      { clearSelectionEvents(hist.undone); }
  }

  function pushSelectionToHistory(sel, dest) {
    var top = lst(dest);
    if (!(top && top.ranges && top.equals(sel)))
      { dest.push(sel); }
  }

  // Used to store marked span information in the history.
  function attachLocalSpans(doc, change, from, to) {
    var existing = change["spans_" + doc.id], n = 0;
    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
      if (line.markedSpans)
        { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
      ++n;
    });
  }

  // When un/re-doing restores text containing marked spans, those
  // that have been explicitly cleared should not be restored.
  function removeClearedSpans(spans) {
    if (!spans) { return null }
    var out;
    for (var i = 0; i < spans.length; ++i) {
      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
      else if (out) { out.push(spans[i]); }
    }
    return !out ? spans : out.length ? out : null
  }

  // Retrieve and filter the old marked spans stored in a change event.
  function getOldSpans(doc, change) {
    var found = change["spans_" + doc.id];
    if (!found) { return null }
    var nw = [];
    for (var i = 0; i < change.text.length; ++i)
      { nw.push(removeClearedSpans(found[i])); }
    return nw
  }

  // Used for un/re-doing changes from the history. Combines the
  // result of computing the existing spans with the set of spans that
  // existed in the history (so that deleting around a span and then
  // undoing brings back the span).
  function mergeOldSpans(doc, change) {
    var old = getOldSpans(doc, change);
    var stretched = stretchSpansOverChange(doc, change);
    if (!old) { return stretched }
    if (!stretched) { return old }

    for (var i = 0; i < old.length; ++i) {
      var oldCur = old[i], stretchCur = stretched[i];
      if (oldCur && stretchCur) {
        spans: for (var j = 0; j < stretchCur.length; ++j) {
          var span = stretchCur[j];
          for (var k = 0; k < oldCur.length; ++k)
            { if (oldCur[k].marker == span.marker) { continue spans } }
          oldCur.push(span);
        }
      } else if (stretchCur) {
        old[i] = stretchCur;
      }
    }
    return old
  }

  // Used both to provide a JSON-safe object in .getHistory, and, when
  // detaching a document, to split the history in two
  function copyHistoryArray(events, newGroup, instantiateSel) {
    var copy = [];
    for (var i = 0; i < events.length; ++i) {
      var event = events[i];
      if (event.ranges) {
        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
        continue
      }
      var changes = event.changes, newChanges = [];
      copy.push({changes: newChanges});
      for (var j = 0; j < changes.length; ++j) {
        var change = changes[j], m = (void 0);
        newChanges.push({from: change.from, to: change.to, text: change.text});
        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
          if (indexOf(newGroup, Number(m[1])) > -1) {
            lst(newChanges)[prop] = change[prop];
            delete change[prop];
          }
        } } }
      }
    }
    return copy
  }

  // The 'scroll' parameter given to many of these indicated whether
  // the new cursor position should be scrolled into view after
  // modifying the selection.

  // If shift is held or the extend flag is set, extends a range to
  // include a given position (and optionally a second position).
  // Otherwise, simply returns the range between the given positions.
  // Used for cursor motion and such.
  function extendRange(range, head, other, extend) {
    if (extend) {
      var anchor = range.anchor;
      if (other) {
        var posBefore = cmp(head, anchor) < 0;
        if (posBefore != (cmp(other, anchor) < 0)) {
          anchor = head;
          head = other;
        } else if (posBefore != (cmp(head, other) < 0)) {
          head = other;
        }
      }
      return new Range(anchor, head)
    } else {
      return new Range(other || head, head)
    }
  }

  // Extend the primary selection range, discard the rest.
  function extendSelection(doc, head, other, options, extend) {
    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
  }

  // Extend all selections (pos is an array of selections with length
  // equal the number of selections)
  function extendSelections(doc, heads, options) {
    var out = [];
    var extend = doc.cm && (doc.cm.display.shift || doc.extend);
    for (var i = 0; i < doc.sel.ranges.length; i++)
      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
    setSelection(doc, newSel, options);
  }

  // Updates a single range in the selection.
  function replaceOneSelection(doc, i, range, options) {
    var ranges = doc.sel.ranges.slice(0);
    ranges[i] = range;
    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
  }

  // Reset the selection to a single range.
  function setSimpleSelection(doc, anchor, head, options) {
    setSelection(doc, simpleSelection(anchor, head), options);
  }

  // Give beforeSelectionChange handlers a change to influence a
  // selection update.
  function filterSelectionChange(doc, sel, options) {
    var obj = {
      ranges: sel.ranges,
      update: function(ranges) {
        var this$1 = this;

        this.ranges = [];
        for (var i = 0; i < ranges.length; i++)
          { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
                                     clipPos(doc, ranges[i].head)); }
      },
      origin: options && options.origin
    };
    signal(doc, "beforeSelectionChange", doc, obj);
    if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
    else { return sel }
  }

  function setSelectionReplaceHistory(doc, sel, options) {
    var done = doc.history.done, last = lst(done);
    if (last && last.ranges) {
      done[done.length - 1] = sel;
      setSelectionNoUndo(doc, sel, options);
    } else {
      setSelection(doc, sel, options);
    }
  }

  // Set a new selection.
  function setSelection(doc, sel, options) {
    setSelectionNoUndo(doc, sel, options);
    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
  }

  function setSelectionNoUndo(doc, sel, options) {
    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
      { sel = filterSelectionChange(doc, sel, options); }

    var bias = options && options.bias ||
      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));

    if (!(options && options.scroll === false) && doc.cm)
      { ensureCursorVisible(doc.cm); }
  }

  function setSelectionInner(doc, sel) {
    if (sel.equals(doc.sel)) { return }

    doc.sel = sel;

    if (doc.cm) {
      doc.cm.curOp.updateInput = 1;
      doc.cm.curOp.selectionChanged = true;
      signalCursorActivity(doc.cm);
    }
    signalLater(doc, "cursorActivity", doc);
  }

  // Verify that the selection does not partially select any atomic
  // marked ranges.
  function reCheckSelection(doc) {
    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
  }

  // Return a selection that does not partially select any atomic
  // ranges.
  function skipAtomicInSelection(doc, sel, bias, mayClear) {
    var out;
    for (var i = 0; i < sel.ranges.length; i++) {
      var range = sel.ranges[i];
      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
      var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
      if (out || newAnchor != range.anchor || newHead != range.head) {
        if (!out) { out = sel.ranges.slice(0, i); }
        out[i] = new Range(newAnchor, newHead);
      }
    }
    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
  }

  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
    var line = getLine(doc, pos.line);
    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
      var sp = line.markedSpans[i], m = sp.marker;

      // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
      // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
      // is with selectLeft/Right
      var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
      var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;

      if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
          (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
        if (mayClear) {
          signal(m, "beforeCursorEnter");
          if (m.explicitlyCleared) {
            if (!line.markedSpans) { break }
            else {--i; continue}
          }
        }
        if (!m.atomic) { continue }

        if (oldPos) {
          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
          if (dir < 0 ? preventCursorRight : preventCursorLeft)
            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
            { return skipAtomicInner(doc, near, pos, dir, mayClear) }
        }

        var far = m.find(dir < 0 ? -1 : 1);
        if (dir < 0 ? preventCursorLeft : preventCursorRight)
          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
      }
    } }
    return pos
  }

  // Ensure a given position is not inside an atomic range.
  function skipAtomic(doc, pos, oldPos, bias, mayClear) {
    var dir = bias || 1;
    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
    if (!found) {
      doc.cantEdit = true;
      return Pos(doc.first, 0)
    }
    return found
  }

  function movePos(doc, pos, dir, line) {
    if (dir < 0 && pos.ch == 0) {
      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
      else { return null }
    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
      else { return null }
    } else {
      return new Pos(pos.line, pos.ch + dir)
    }
  }

  function selectAll(cm) {
    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
  }

  // UPDATING

  // Allow "beforeChange" event handlers to influence a change
  function filterChange(doc, change, update) {
    var obj = {
      canceled: false,
      from: change.from,
      to: change.to,
      text: change.text,
      origin: change.origin,
      cancel: function () { return obj.canceled = true; }
    };
    if (update) { obj.update = function (from, to, text, origin) {
      if (from) { obj.from = clipPos(doc, from); }
      if (to) { obj.to = clipPos(doc, to); }
      if (text) { obj.text = text; }
      if (origin !== undefined) { obj.origin = origin; }
    }; }
    signal(doc, "beforeChange", doc, obj);
    if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }

    if (obj.canceled) {
      if (doc.cm) { doc.cm.curOp.updateInput = 2; }
      return null
    }
    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
  }

  // Apply a change to a document, and add it to the document's
  // history, and propagating it to all linked documents.
  function makeChange(doc, change, ignoreReadOnly) {
    if (doc.cm) {
      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
      if (doc.cm.state.suppressEdits) { return }
    }

    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
      change = filterChange(doc, change, true);
      if (!change) { return }
    }

    // Possibly split or suppress the update based on the presence
    // of read-only spans in its range.
    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
    if (split) {
      for (var i = split.length - 1; i >= 0; --i)
        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
    } else {
      makeChangeInner(doc, change);
    }
  }

  function makeChangeInner(doc, change) {
    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
    var selAfter = computeSelAfterChange(doc, change);
    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);

    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
    var rebased = [];

    linkedDocs(doc, function (doc, sharedHist) {
      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
        rebaseHist(doc.history, change);
        rebased.push(doc.history);
      }
      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
    });
  }

  // Revert a change stored in a document's history.
  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
    var suppress = doc.cm && doc.cm.state.suppressEdits;
    if (suppress && !allowSelectionOnly) { return }

    var hist = doc.history, event, selAfter = doc.sel;
    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;

    // Verify that there is a useable event (so that ctrl-z won't
    // needlessly clear selection events)
    var i = 0;
    for (; i < source.length; i++) {
      event = source[i];
      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
        { break }
    }
    if (i == source.length) { return }
    hist.lastOrigin = hist.lastSelOrigin = null;

    for (;;) {
      event = source.pop();
      if (event.ranges) {
        pushSelectionToHistory(event, dest);
        if (allowSelectionOnly && !event.equals(doc.sel)) {
          setSelection(doc, event, {clearRedo: false});
          return
        }
        selAfter = event;
      } else if (suppress) {
        source.push(event);
        return
      } else { break }
    }

    // Build up a reverse change object to add to the opposite history
    // stack (redo when undoing, and vice versa).
    var antiChanges = [];
    pushSelectionToHistory(selAfter, dest);
    dest.push({changes: antiChanges, generation: hist.generation});
    hist.generation = event.generation || ++hist.maxGeneration;

    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");

    var loop = function ( i ) {
      var change = event.changes[i];
      change.origin = type;
      if (filter && !filterChange(doc, change, false)) {
        source.length = 0;
        return {}
      }

      antiChanges.push(historyChangeFromChange(doc, change));

      var after = i ? computeSelAfterChange(doc, change) : lst(source);
      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
      var rebased = [];

      // Propagate to the linked documents
      linkedDocs(doc, function (doc, sharedHist) {
        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
          rebaseHist(doc.history, change);
          rebased.push(doc.history);
        }
        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
      });
    };

    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
      var returned = loop( i$1 );

      if ( returned ) return returned.v;
    }
  }

  // Sub-views need their line numbers shifted when text is added
  // above or below them in the parent document.
  function shiftDoc(doc, distance) {
    if (distance == 0) { return }
    doc.first += distance;
    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
      Pos(range.anchor.line + distance, range.anchor.ch),
      Pos(range.head.line + distance, range.head.ch)
    ); }), doc.sel.primIndex);
    if (doc.cm) {
      regChange(doc.cm, doc.first, doc.first - distance, distance);
      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
        { regLineChange(doc.cm, l, "gutter"); }
    }
  }

  // More lower-level change function, handling only a single document
  // (not linked ones).
  function makeChangeSingleDoc(doc, change, selAfter, spans) {
    if (doc.cm && !doc.cm.curOp)
      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }

    if (change.to.line < doc.first) {
      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
      return
    }
    if (change.from.line > doc.lastLine()) { return }

    // Clip the change to the size of this doc
    if (change.from.line < doc.first) {
      var shift = change.text.length - 1 - (doc.first - change.from.line);
      shiftDoc(doc, shift);
      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
                text: [lst(change.text)], origin: change.origin};
    }
    var last = doc.lastLine();
    if (change.to.line > last) {
      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
                text: [change.text[0]], origin: change.origin};
    }

    change.removed = getBetween(doc, change.from, change.to);

    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
    else { updateDoc(doc, change, spans); }
    setSelectionNoUndo(doc, selAfter, sel_dontScroll);

    if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
      { doc.cantEdit = false; }
  }

  // Handle the interaction of a change to a document with the editor
  // that this document is part of.
  function makeChangeSingleDocInEditor(cm, change, spans) {
    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;

    var recomputeMaxLength = false, checkWidthStart = from.line;
    if (!cm.options.lineWrapping) {
      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
      doc.iter(checkWidthStart, to.line + 1, function (line) {
        if (line == display.maxLine) {
          recomputeMaxLength = true;
          return true
        }
      });
    }

    if (doc.sel.contains(change.from, change.to) > -1)
      { signalCursorActivity(cm); }

    updateDoc(doc, change, spans, estimateHeight(cm));

    if (!cm.options.lineWrapping) {
      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
        var len = lineLength(line);
        if (len > display.maxLineLength) {
          display.maxLine = line;
          display.maxLineLength = len;
          display.maxLineChanged = true;
          recomputeMaxLength = false;
        }
      });
      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
    }

    retreatFrontier(doc, from.line);
    startWorker(cm, 400);

    var lendiff = change.text.length - (to.line - from.line) - 1;
    // Remember that these lines changed, for updating the display
    if (change.full)
      { regChange(cm); }
    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
      { regLineChange(cm, from.line, "text"); }
    else
      { regChange(cm, from.line, to.line + 1, lendiff); }

    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
    if (changeHandler || changesHandler) {
      var obj = {
        from: from, to: to,
        text: change.text,
        removed: change.removed,
        origin: change.origin
      };
      if (changeHandler) { signalLater(cm, "change", cm, obj); }
      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
    }
    cm.display.selForContextMenu = null;
  }

  function replaceRange(doc, code, from, to, origin) {
    var assign;

    if (!to) { to = from; }
    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
    if (typeof code == "string") { code = doc.splitLines(code); }
    makeChange(doc, {from: from, to: to, text: code, origin: origin});
  }

  // Rebasing/resetting history to deal with externally-sourced changes

  function rebaseHistSelSingle(pos, from, to, diff) {
    if (to < pos.line) {
      pos.line += diff;
    } else if (from < pos.line) {
      pos.line = from;
      pos.ch = 0;
    }
  }

  // Tries to rebase an array of history events given a change in the
  // document. If the change touches the same lines as the event, the
  // event, and everything 'behind' it, is discarded. If the change is
  // before the event, the event's positions are updated. Uses a
  // copy-on-write scheme for the positions, to avoid having to
  // reallocate them all on every rebase, but also avoid problems with
  // shared position objects being unsafely updated.
  function rebaseHistArray(array, from, to, diff) {
    for (var i = 0; i < array.length; ++i) {
      var sub = array[i], ok = true;
      if (sub.ranges) {
        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
        for (var j = 0; j < sub.ranges.length; j++) {
          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
        }
        continue
      }
      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
        var cur = sub.changes[j$1];
        if (to < cur.from.line) {
          cur.from = Pos(cur.from.line + diff, cur.from.ch);
          cur.to = Pos(cur.to.line + diff, cur.to.ch);
        } else if (from <= cur.to.line) {
          ok = false;
          break
        }
      }
      if (!ok) {
        array.splice(0, i + 1);
        i = 0;
      }
    }
  }

  function rebaseHist(hist, change) {
    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
    rebaseHistArray(hist.done, from, to, diff);
    rebaseHistArray(hist.undone, from, to, diff);
  }

  // Utility for applying a change to a line by handle or number,
  // returning the number and optionally registering the line as
  // changed.
  function changeLine(doc, handle, changeType, op) {
    var no = handle, line = handle;
    if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
    else { no = lineNo(handle); }
    if (no == null) { return null }
    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
    return line
  }

  // The document is represented as a BTree consisting of leaves, with
  // chunk of lines in them, and branches, with up to ten leaves or
  // other branch nodes below them. The top node is always a branch
  // node, and is the document object itself (meaning it has
  // additional methods and properties).
  //
  // All nodes have parent links. The tree is used both to go from
  // line numbers to line objects, and to go from objects to numbers.
  // It also indexes by height, and is used to convert between height
  // and line object, and to find the total height of the document.
  //
  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html

  function LeafChunk(lines) {
    var this$1 = this;

    this.lines = lines;
    this.parent = null;
    var height = 0;
    for (var i = 0; i < lines.length; ++i) {
      lines[i].parent = this$1;
      height += lines[i].height;
    }
    this.height = height;
  }

  LeafChunk.prototype = {
    chunkSize: function() { return this.lines.length },

    // Remove the n lines at offset 'at'.
    removeInner: function(at, n) {
      var this$1 = this;

      for (var i = at, e = at + n; i < e; ++i) {
        var line = this$1.lines[i];
        this$1.height -= line.height;
        cleanUpLine(line);
        signalLater(line, "delete");
      }
      this.lines.splice(at, n);
    },

    // Helper used to collapse a small branch into a single leaf.
    collapse: function(lines) {
      lines.push.apply(lines, this.lines);
    },

    // Insert the given array of lines at offset 'at', count them as
    // having the given height.
    insertInner: function(at, lines, height) {
      var this$1 = this;

      this.height += height;
      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
    },

    // Used to iterate over a part of the tree.
    iterN: function(at, n, op) {
      var this$1 = this;

      for (var e = at + n; at < e; ++at)
        { if (op(this$1.lines[at])) { return true } }
    }
  };

  function BranchChunk(children) {
    var this$1 = this;

    this.children = children;
    var size = 0, height = 0;
    for (var i = 0; i < children.length; ++i) {
      var ch = children[i];
      size += ch.chunkSize(); height += ch.height;
      ch.parent = this$1;
    }
    this.size = size;
    this.height = height;
    this.parent = null;
  }

  BranchChunk.prototype = {
    chunkSize: function() { return this.size },

    removeInner: function(at, n) {
      var this$1 = this;

      this.size -= n;
      for (var i = 0; i < this.children.length; ++i) {
        var child = this$1.children[i], sz = child.chunkSize();
        if (at < sz) {
          var rm = Math.min(n, sz - at), oldHeight = child.height;
          child.removeInner(at, rm);
          this$1.height -= oldHeight - child.height;
          if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
          if ((n -= rm) == 0) { break }
          at = 0;
        } else { at -= sz; }
      }
      // If the result is smaller than 25 lines, ensure that it is a
      // single leaf node.
      if (this.size - n < 25 &&
          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
        var lines = [];
        this.collapse(lines);
        this.children = [new LeafChunk(lines)];
        this.children[0].parent = this;
      }
    },

    collapse: function(lines) {
      var this$1 = this;

      for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
    },

    insertInner: function(at, lines, height) {
      var this$1 = this;

      this.size += lines.length;
      this.height += height;
      for (var i = 0; i < this.children.length; ++i) {
        var child = this$1.children[i], sz = child.chunkSize();
        if (at <= sz) {
          child.insertInner(at, lines, height);
          if (child.lines && child.lines.length > 50) {
            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
            var remaining = child.lines.length % 25 + 25;
            for (var pos = remaining; pos < child.lines.length;) {
              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
              child.height -= leaf.height;
              this$1.children.splice(++i, 0, leaf);
              leaf.parent = this$1;
            }
            child.lines = child.lines.slice(0, remaining);
            this$1.maybeSpill();
          }
          break
        }
        at -= sz;
      }
    },

    // When a node has grown, check whether it should be split.
    maybeSpill: function() {
      if (this.children.length <= 10) { return }
      var me = this;
      do {
        var spilled = me.children.splice(me.children.length - 5, 5);
        var sibling = new BranchChunk(spilled);
        if (!me.parent) { // Become the parent node
          var copy = new BranchChunk(me.children);
          copy.parent = me;
          me.children = [copy, sibling];
          me = copy;
       } else {
          me.size -= sibling.size;
          me.height -= sibling.height;
          var myIndex = indexOf(me.parent.children, me);
          me.parent.children.splice(myIndex + 1, 0, sibling);
        }
        sibling.parent = me.parent;
      } while (me.children.length > 10)
      me.parent.maybeSpill();
    },

    iterN: function(at, n, op) {
      var this$1 = this;

      for (var i = 0; i < this.children.length; ++i) {
        var child = this$1.children[i], sz = child.chunkSize();
        if (at < sz) {
          var used = Math.min(n, sz - at);
          if (child.iterN(at, used, op)) { return true }
          if ((n -= used) == 0) { break }
          at = 0;
        } else { at -= sz; }
      }
    }
  };

  // Line widgets are block elements displayed above or below a line.

  var LineWidget = function(doc, node, options) {
    var this$1 = this;

    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
      { this$1[opt] = options[opt]; } } }
    this.doc = doc;
    this.node = node;
  };

  LineWidget.prototype.clear = function () {
      var this$1 = this;

    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
    if (no == null || !ws) { return }
    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
    if (!ws.length) { line.widgets = null; }
    var height = widgetHeight(this);
    updateLineHeight(line, Math.max(0, line.height - height));
    if (cm) {
      runInOp(cm, function () {
        adjustScrollWhenAboveVisible(cm, line, -height);
        regLineChange(cm, no, "widget");
      });
      signalLater(cm, "lineWidgetCleared", cm, this, no);
    }
  };

  LineWidget.prototype.changed = function () {
      var this$1 = this;

    var oldH = this.height, cm = this.doc.cm, line = this.line;
    this.height = null;
    var diff = widgetHeight(this) - oldH;
    if (!diff) { return }
    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
    if (cm) {
      runInOp(cm, function () {
        cm.curOp.forceUpdate = true;
        adjustScrollWhenAboveVisible(cm, line, diff);
        signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
      });
    }
  };
  eventMixin(LineWidget);

  function adjustScrollWhenAboveVisible(cm, line, diff) {
    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
      { addToScrollTop(cm, diff); }
  }

  function addLineWidget(doc, handle, node, options) {
    var widget = new LineWidget(doc, node, options);
    var cm = doc.cm;
    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
    changeLine(doc, handle, "widget", function (line) {
      var widgets = line.widgets || (line.widgets = []);
      if (widget.insertAt == null) { widgets.push(widget); }
      else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
      widget.line = line;
      if (cm && !lineIsHidden(doc, line)) {
        var aboveVisible = heightAtLine(line) < doc.scrollTop;
        updateLineHeight(line, line.height + widgetHeight(widget));
        if (aboveVisible) { addToScrollTop(cm, widget.height); }
        cm.curOp.forceUpdate = true;
      }
      return true
    });
    if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
    return widget
  }

  // TEXTMARKERS

  // Created with markText and setBookmark methods. A TextMarker is a
  // handle that can be used to clear or find a marked position in the
  // document. Line objects hold arrays (markedSpans) containing
  // {from, to, marker} object pointing to such marker objects, and
  // indicating that such a marker is present on that line. Multiple
  // lines may point to the same marker when it spans across lines.
  // The spans will have null for their from/to properties when the
  // marker continues beyond the start/end of the line. Markers have
  // links back to the lines they currently touch.

  // Collapsed markers have unique ids, in order to be able to order
  // them, which is needed for uniquely determining an outer marker
  // when they overlap (they may nest, but not partially overlap).
  var nextMarkerId = 0;

  var TextMarker = function(doc, type) {
    this.lines = [];
    this.type = type;
    this.doc = doc;
    this.id = ++nextMarkerId;
  };

  // Clear the marker.
  TextMarker.prototype.clear = function () {
      var this$1 = this;

    if (this.explicitlyCleared) { return }
    var cm = this.doc.cm, withOp = cm && !cm.curOp;
    if (withOp) { startOperation(cm); }
    if (hasHandler(this, "clear")) {
      var found = this.find();
      if (found) { signalLater(this, "clear", found.from, found.to); }
    }
    var min = null, max = null;
    for (var i = 0; i < this.lines.length; ++i) {
      var line = this$1.lines[i];
      var span = getMarkedSpanFor(line.markedSpans, this$1);
      if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
      else if (cm) {
        if (span.to != null) { max = lineNo(line); }
        if (span.from != null) { min = lineNo(line); }
      }
      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
      if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
        { updateLineHeight(line, textHeight(cm.display)); }
    }
    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
      var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
      if (len > cm.display.maxLineLength) {
        cm.display.maxLine = visual;
        cm.display.maxLineLength = len;
        cm.display.maxLineChanged = true;
      }
    } }

    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
    this.lines.length = 0;
    this.explicitlyCleared = true;
    if (this.atomic && this.doc.cantEdit) {
      this.doc.cantEdit = false;
      if (cm) { reCheckSelection(cm.doc); }
    }
    if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
    if (withOp) { endOperation(cm); }
    if (this.parent) { this.parent.clear(); }
  };

  // Find the position of the marker in the document. Returns a {from,
  // to} object by default. Side can be passed to get a specific side
  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
  // Pos objects returned contain a line object, rather than a line
  // number (used to prevent looking up the same line twice).
  TextMarker.prototype.find = function (side, lineObj) {
      var this$1 = this;

    if (side == null && this.type == "bookmark") { side = 1; }
    var from, to;
    for (var i = 0; i < this.lines.length; ++i) {
      var line = this$1.lines[i];
      var span = getMarkedSpanFor(line.markedSpans, this$1);
      if (span.from != null) {
        from = Pos(lineObj ? line : lineNo(line), span.from);
        if (side == -1) { return from }
      }
      if (span.to != null) {
        to = Pos(lineObj ? line : lineNo(line), span.to);
        if (side == 1) { return to }
      }
    }
    return from && {from: from, to: to}
  };

  // Signals that the marker's widget changed, and surrounding layout
  // should be recomputed.
  TextMarker.prototype.changed = function () {
      var this$1 = this;

    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
    if (!pos || !cm) { return }
    runInOp(cm, function () {
      var line = pos.line, lineN = lineNo(pos.line);
      var view = findViewForLine(cm, lineN);
      if (view) {
        clearLineMeasurementCacheFor(view);
        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
      }
      cm.curOp.updateMaxLine = true;
      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
        var oldHeight = widget.height;
        widget.height = null;
        var dHeight = widgetHeight(widget) - oldHeight;
        if (dHeight)
          { updateLineHeight(line, line.height + dHeight); }
      }
      signalLater(cm, "markerChanged", cm, this$1);
    });
  };

  TextMarker.prototype.attachLine = function (line) {
    if (!this.lines.length && this.doc.cm) {
      var op = this.doc.cm.curOp;
      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
    }
    this.lines.push(line);
  };

  TextMarker.prototype.detachLine = function (line) {
    this.lines.splice(indexOf(this.lines, line), 1);
    if (!this.lines.length && this.doc.cm) {
      var op = this.doc.cm.curOp
      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
    }
  };
  eventMixin(TextMarker);

  // Create a marker, wire it up to the right lines, and
  function markText(doc, from, to, options, type) {
    // Shared markers (across linked documents) are handled separately
    // (markTextShared will call out to this again, once per
    // document).
    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
    // Ensure we are in an operation.
    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }

    var marker = new TextMarker(doc, type), diff = cmp(from, to);
    if (options) { copyObj(options, marker, false); }
    // Don't connect empty markers unless clearWhenEmpty is false
    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
      { return marker }
    if (marker.replacedWith) {
      // Showing up as a widget implies collapsed (widget replaces text)
      marker.collapsed = true;
      marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
    }
    if (marker.collapsed) {
      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
        { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
      seeCollapsedSpans();
    }

    if (marker.addToHistory)
      { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }

    var curLine = from.line, cm = doc.cm, updateMaxLine;
    doc.iter(curLine, to.line + 1, function (line) {
      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
        { updateMaxLine = true; }
      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
      addMarkedSpan(line, new MarkedSpan(marker,
                                         curLine == from.line ? from.ch : null,
                                         curLine == to.line ? to.ch : null));
      ++curLine;
    });
    // lineIsHidden depends on the presence of the spans, so needs a second pass
    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
    }); }

    if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }

    if (marker.readOnly) {
      seeReadOnlySpans();
      if (doc.history.done.length || doc.history.undone.length)
        { doc.clearHistory(); }
    }
    if (marker.collapsed) {
      marker.id = ++nextMarkerId;
      marker.atomic = true;
    }
    if (cm) {
      // Sync editor state
      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
      if (marker.collapsed)
        { regChange(cm, from.line, to.line + 1); }
      else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
               marker.attributes || marker.title)
        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
      if (marker.atomic) { reCheckSelection(cm.doc); }
      signalLater(cm, "markerAdded", cm, marker);
    }
    return marker
  }

  // SHARED TEXTMARKERS

  // A shared marker spans multiple linked documents. It is
  // implemented as a meta-marker-object controlling multiple normal
  // markers.
  var SharedTextMarker = function(markers, primary) {
    var this$1 = this;

    this.markers = markers;
    this.primary = primary;
    for (var i = 0; i < markers.length; ++i)
      { markers[i].parent = this$1; }
  };

  SharedTextMarker.prototype.clear = function () {
      var this$1 = this;

    if (this.explicitlyCleared) { return }
    this.explicitlyCleared = true;
    for (var i = 0; i < this.markers.length; ++i)
      { this$1.markers[i].clear(); }
    signalLater(this, "clear");
  };

  SharedTextMarker.prototype.find = function (side, lineObj) {
    return this.primary.find(side, lineObj)
  };
  eventMixin(SharedTextMarker);

  function markTextShared(doc, from, to, options, type) {
    options = copyObj(options);
    options.shared = false;
    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
    var widget = options.widgetNode;
    linkedDocs(doc, function (doc) {
      if (widget) { options.widgetNode = widget.cloneNode(true); }
      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
      for (var i = 0; i < doc.linked.length; ++i)
        { if (doc.linked[i].isParent) { return } }
      primary = lst(markers);
    });
    return new SharedTextMarker(markers, primary)
  }

  function findSharedMarkers(doc) {
    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
  }

  function copySharedMarkers(doc, markers) {
    for (var i = 0; i < markers.length; i++) {
      var marker = markers[i], pos = marker.find();
      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
      if (cmp(mFrom, mTo)) {
        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
        marker.markers.push(subMark);
        subMark.parent = marker;
      }
    }
  }

  function detachSharedMarkers(markers) {
    var loop = function ( i ) {
      var marker = markers[i], linked = [marker.primary.doc];
      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
      for (var j = 0; j < marker.markers.length; j++) {
        var subMarker = marker.markers[j];
        if (indexOf(linked, subMarker.doc) == -1) {
          subMarker.parent = null;
          marker.markers.splice(j--, 1);
        }
      }
    };

    for (var i = 0; i < markers.length; i++) loop( i );
  }

  var nextDocId = 0;
  var Doc = function(text, mode, firstLine, lineSep, direction) {
    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
    if (firstLine == null) { firstLine = 0; }

    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
    this.first = firstLine;
    this.scrollTop = this.scrollLeft = 0;
    this.cantEdit = false;
    this.cleanGeneration = 1;
    this.modeFrontier = this.highlightFrontier = firstLine;
    var start = Pos(firstLine, 0);
    this.sel = simpleSelection(start);
    this.history = new History(null);
    this.id = ++nextDocId;
    this.modeOption = mode;
    this.lineSep = lineSep;
    this.direction = (direction == "rtl") ? "rtl" : "ltr";
    this.extend = false;

    if (typeof text == "string") { text = this.splitLines(text); }
    updateDoc(this, {from: start, to: start, text: text});
    setSelection(this, simpleSelection(start), sel_dontScroll);
  };

  Doc.prototype = createObj(BranchChunk.prototype, {
    constructor: Doc,
    // Iterate over the document. Supports two forms -- with only one
    // argument, it calls that for each line in the document. With
    // three, it iterates over the range given by the first two (with
    // the second being non-inclusive).
    iter: function(from, to, op) {
      if (op) { this.iterN(from - this.first, to - from, op); }
      else { this.iterN(this.first, this.first + this.size, from); }
    },

    // Non-public interface for adding and removing lines.
    insert: function(at, lines) {
      var height = 0;
      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
      this.insertInner(at - this.first, lines, height);
    },
    remove: function(at, n) { this.removeInner(at - this.first, n); },

    // From here, the methods are part of the public interface. Most
    // are also available from CodeMirror (editor) instances.

    getValue: function(lineSep) {
      var lines = getLines(this, this.first, this.first + this.size);
      if (lineSep === false) { return lines }
      return lines.join(lineSep || this.lineSeparator())
    },
    setValue: docMethodOp(function(code) {
      var top = Pos(this.first, 0), last = this.first + this.size - 1;
      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
                        text: this.splitLines(code), origin: "setValue", full: true}, true);
      if (this.cm) { scrollToCoords(this.cm, 0, 0); }
      setSelection(this, simpleSelection(top), sel_dontScroll);
    }),
    replaceRange: function(code, from, to, origin) {
      from = clipPos(this, from);
      to = to ? clipPos(this, to) : from;
      replaceRange(this, code, from, to, origin);
    },
    getRange: function(from, to, lineSep) {
      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
      if (lineSep === false) { return lines }
      return lines.join(lineSep || this.lineSeparator())
    },

    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},

    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
    getLineNumber: function(line) {return lineNo(line)},

    getLineHandleVisualStart: function(line) {
      if (typeof line == "number") { line = getLine(this, line); }
      return visualLine(line)
    },

    lineCount: function() {return this.size},
    firstLine: function() {return this.first},
    lastLine: function() {return this.first + this.size - 1},

    clipPos: function(pos) {return clipPos(this, pos)},

    getCursor: function(start) {
      var range$$1 = this.sel.primary(), pos;
      if (start == null || start == "head") { pos = range$$1.head; }
      else if (start == "anchor") { pos = range$$1.anchor; }
      else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
      else { pos = range$$1.from(); }
      return pos
    },
    listSelections: function() { return this.sel.ranges },
    somethingSelected: function() {return this.sel.somethingSelected()},

    setCursor: docMethodOp(function(line, ch, options) {
      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
    }),
    setSelection: docMethodOp(function(anchor, head, options) {
      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
    }),
    extendSelection: docMethodOp(function(head, other, options) {
      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
    }),
    extendSelections: docMethodOp(function(heads, options) {
      extendSelections(this, clipPosArray(this, heads), options);
    }),
    extendSelectionsBy: docMethodOp(function(f, options) {
      var heads = map(this.sel.ranges, f);
      extendSelections(this, clipPosArray(this, heads), options);
    }),
    setSelections: docMethodOp(function(ranges, primary, options) {
      var this$1 = this;

      if (!ranges.length) { return }
      var out = [];
      for (var i = 0; i < ranges.length; i++)
        { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
                           clipPos(this$1, ranges[i].head)); }
      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
      setSelection(this, normalizeSelection(this.cm, out, primary), options);
    }),
    addSelection: docMethodOp(function(anchor, head, options) {
      var ranges = this.sel.ranges.slice(0);
      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
    }),

    getSelection: function(lineSep) {
      var this$1 = this;

      var ranges = this.sel.ranges, lines;
      for (var i = 0; i < ranges.length; i++) {
        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
        lines = lines ? lines.concat(sel) : sel;
      }
      if (lineSep === false) { return lines }
      else { return lines.join(lineSep || this.lineSeparator()) }
    },
    getSelections: function(lineSep) {
      var this$1 = this;

      var parts = [], ranges = this.sel.ranges;
      for (var i = 0; i < ranges.length; i++) {
        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
        if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
        parts[i] = sel;
      }
      return parts
    },
    replaceSelection: function(code, collapse, origin) {
      var dup = [];
      for (var i = 0; i < this.sel.ranges.length; i++)
        { dup[i] = code; }
      this.replaceSelections(dup, collapse, origin || "+input");
    },
    replaceSelections: docMethodOp(function(code, collapse, origin) {
      var this$1 = this;

      var changes = [], sel = this.sel;
      for (var i = 0; i < sel.ranges.length; i++) {
        var range$$1 = sel.ranges[i];
        changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
      }
      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
        { makeChange(this$1, changes[i$1]); }
      if (newSel) { setSelectionReplaceHistory(this, newSel); }
      else if (this.cm) { ensureCursorVisible(this.cm); }
    }),
    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),

    setExtending: function(val) {this.extend = val;},
    getExtending: function() {return this.extend},

    historySize: function() {
      var hist = this.history, done = 0, undone = 0;
      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
      return {undo: done, redo: undone}
    },
    clearHistory: function() {
      var this$1 = this;

      this.history = new History(this.history.maxGeneration);
      linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);
    },

    markClean: function() {
      this.cleanGeneration = this.changeGeneration(true);
    },
    changeGeneration: function(forceSplit) {
      if (forceSplit)
        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
      return this.history.generation
    },
    isClean: function (gen) {
      return this.history.generation == (gen || this.cleanGeneration)
    },

    getHistory: function() {
      return {done: copyHistoryArray(this.history.done),
              undone: copyHistoryArray(this.history.undone)}
    },
    setHistory: function(histData) {
      var hist = this.history = new History(this.history.maxGeneration);
      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
    },

    setGutterMarker: docMethodOp(function(line, gutterID, value) {
      return changeLine(this, line, "gutter", function (line) {
        var markers = line.gutterMarkers || (line.gutterMarkers = {});
        markers[gutterID] = value;
        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
        return true
      })
    }),

    clearGutter: docMethodOp(function(gutterID) {
      var this$1 = this;

      this.iter(function (line) {
        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
          changeLine(this$1, line, "gutter", function () {
            line.gutterMarkers[gutterID] = null;
            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
            return true
          });
        }
      });
    }),

    lineInfo: function(line) {
      var n;
      if (typeof line == "number") {
        if (!isLine(this, line)) { return null }
        n = line;
        line = getLine(this, line);
        if (!line) { return null }
      } else {
        n = lineNo(line);
        if (n == null) { return null }
      }
      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
              widgets: line.widgets}
    },

    addLineClass: docMethodOp(function(handle, where, cls) {
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
        var prop = where == "text" ? "textClass"
                 : where == "background" ? "bgClass"
                 : where == "gutter" ? "gutterClass" : "wrapClass";
        if (!line[prop]) { line[prop] = cls; }
        else if (classTest(cls).test(line[prop])) { return false }
        else { line[prop] += " " + cls; }
        return true
      })
    }),
    removeLineClass: docMethodOp(function(handle, where, cls) {
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
        var prop = where == "text" ? "textClass"
                 : where == "background" ? "bgClass"
                 : where == "gutter" ? "gutterClass" : "wrapClass";
        var cur = line[prop];
        if (!cur) { return false }
        else if (cls == null) { line[prop] = null; }
        else {
          var found = cur.match(classTest(cls));
          if (!found) { return false }
          var end = found.index + found[0].length;
          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
        }
        return true
      })
    }),

    addLineWidget: docMethodOp(function(handle, node, options) {
      return addLineWidget(this, handle, node, options)
    }),
    removeLineWidget: function(widget) { widget.clear(); },

    markText: function(from, to, options) {
      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
    },
    setBookmark: function(pos, options) {
      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
                      insertLeft: options && options.insertLeft,
                      clearWhenEmpty: false, shared: options && options.shared,
                      handleMouseEvents: options && options.handleMouseEvents};
      pos = clipPos(this, pos);
      return markText(this, pos, pos, realOpts, "bookmark")
    },
    findMarksAt: function(pos) {
      pos = clipPos(this, pos);
      var markers = [], spans = getLine(this, pos.line).markedSpans;
      if (spans) { for (var i = 0; i < spans.length; ++i) {
        var span = spans[i];
        if ((span.from == null || span.from <= pos.ch) &&
            (span.to == null || span.to >= pos.ch))
          { markers.push(span.marker.parent || span.marker); }
      } }
      return markers
    },
    findMarks: function(from, to, filter) {
      from = clipPos(this, from); to = clipPos(this, to);
      var found = [], lineNo$$1 = from.line;
      this.iter(from.line, to.line + 1, function (line) {
        var spans = line.markedSpans;
        if (spans) { for (var i = 0; i < spans.length; i++) {
          var span = spans[i];
          if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
                span.from == null && lineNo$$1 != from.line ||
                span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
              (!filter || filter(span.marker)))
            { found.push(span.marker.parent || span.marker); }
        } }
        ++lineNo$$1;
      });
      return found
    },
    getAllMarks: function() {
      var markers = [];
      this.iter(function (line) {
        var sps = line.markedSpans;
        if (sps) { for (var i = 0; i < sps.length; ++i)
          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
      });
      return markers
    },

    posFromIndex: function(off) {
      var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
      this.iter(function (line) {
        var sz = line.text.length + sepSize;
        if (sz > off) { ch = off; return true }
        off -= sz;
        ++lineNo$$1;
      });
      return clipPos(this, Pos(lineNo$$1, ch))
    },
    indexFromPos: function (coords) {
      coords = clipPos(this, coords);
      var index = coords.ch;
      if (coords.line < this.first || coords.ch < 0) { return 0 }
      var sepSize = this.lineSeparator().length;
      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
        index += line.text.length + sepSize;
      });
      return index
    },

    copy: function(copyHistory) {
      var doc = new Doc(getLines(this, this.first, this.first + this.size),
                        this.modeOption, this.first, this.lineSep, this.direction);
      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
      doc.sel = this.sel;
      doc.extend = false;
      if (copyHistory) {
        doc.history.undoDepth = this.history.undoDepth;
        doc.setHistory(this.getHistory());
      }
      return doc
    },

    linkedDoc: function(options) {
      if (!options) { options = {}; }
      var from = this.first, to = this.first + this.size;
      if (options.from != null && options.from > from) { from = options.from; }
      if (options.to != null && options.to < to) { to = options.to; }
      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
      if (options.sharedHist) { copy.history = this.history
      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
      copySharedMarkers(copy, findSharedMarkers(this));
      return copy
    },
    unlinkDoc: function(other) {
      var this$1 = this;

      if (other instanceof CodeMirror) { other = other.doc; }
      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
        var link = this$1.linked[i];
        if (link.doc != other) { continue }
        this$1.linked.splice(i, 1);
        other.unlinkDoc(this$1);
        detachSharedMarkers(findSharedMarkers(this$1));
        break
      } }
      // If the histories were shared, split them again
      if (other.history == this.history) {
        var splitIds = [other.id];
        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
        other.history = new History(null);
        other.history.done = copyHistoryArray(this.history.done, splitIds);
        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
      }
    },
    iterLinkedDocs: function(f) {linkedDocs(this, f);},

    getMode: function() {return this.mode},
    getEditor: function() {return this.cm},

    splitLines: function(str) {
      if (this.lineSep) { return str.split(this.lineSep) }
      return splitLinesAuto(str)
    },
    lineSeparator: function() { return this.lineSep || "\n" },

    setDirection: docMethodOp(function (dir) {
      if (dir != "rtl") { dir = "ltr"; }
      if (dir == this.direction) { return }
      this.direction = dir;
      this.iter(function (line) { return line.order = null; });
      if (this.cm) { directionChanged(this.cm); }
    })
  });

  // Public alias.
  Doc.prototype.eachLine = Doc.prototype.iter;

  // Kludge to work around strange IE behavior where it'll sometimes
  // re-fire a series of drag-related events right after the drop (#1551)
  var lastDrop = 0;

  function onDrop(e) {
    var cm = this;
    clearDragCursor(cm);
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
      { return }
    e_preventDefault(e);
    if (ie) { lastDrop = +new Date; }
    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
    if (!pos || cm.isReadOnly()) { return }
    // Might be a file drop, in which case we simply extract the text
    // and insert it.
    if (files && files.length && window.FileReader && window.File) {
      var n = files.length, text = Array(n), read = 0;
      var markAsReadAndPasteIfAllFilesAreRead = function () {
        if (++read == n) {
          operation(cm, function () {
            pos = clipPos(cm.doc, pos);
            var change = {from: pos, to: pos,
                          text: cm.doc.splitLines(
                              text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
                          origin: "paste"};
            makeChange(cm.doc, change);
            setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
          })();
        }
      };
      var readTextFromFile = function (file, i) {
        if (cm.options.allowDropFileTypes &&
            indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
          markAsReadAndPasteIfAllFilesAreRead();
          return
        }
        var reader = new FileReader;
        reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };
        reader.onload = function () {
          var content = reader.result;
          if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
            markAsReadAndPasteIfAllFilesAreRead();
            return
          }
          text[i] = content;
          markAsReadAndPasteIfAllFilesAreRead();
        };
        reader.readAsText(file);
      };
      for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }
    } else { // Normal drop
      // Don't do a replace if the drop happened inside of the selected text.
      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
        cm.state.draggingText(e);
        // Ensure the editor is re-focused
        setTimeout(function () { return cm.display.input.focus(); }, 20);
        return
      }
      try {
        var text$1 = e.dataTransfer.getData("Text");
        if (text$1) {
          var selected;
          if (cm.state.draggingText && !cm.state.draggingText.copy)
            { selected = cm.listSelections(); }
          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
            { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
          cm.replaceSelection(text$1, "around", "paste");
          cm.display.input.focus();
        }
      }
      catch(e){}
    }
  }

  function onDragStart(cm, e) {
    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }

    e.dataTransfer.setData("Text", cm.getSelection());
    e.dataTransfer.effectAllowed = "copyMove";

    // Use dummy image instead of default browsers image.
    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
    if (e.dataTransfer.setDragImage && !safari) {
      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
      if (presto) {
        img.width = img.height = 1;
        cm.display.wrapper.appendChild(img);
        // Force a relayout, or Opera won't use our image for some obscure reason
        img._top = img.offsetTop;
      }
      e.dataTransfer.setDragImage(img, 0, 0);
      if (presto) { img.parentNode.removeChild(img); }
    }
  }

  function onDragOver(cm, e) {
    var pos = posFromMouse(cm, e);
    if (!pos) { return }
    var frag = document.createDocumentFragment();
    drawSelectionCursor(cm, pos, frag);
    if (!cm.display.dragCursor) {
      cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
    }
    removeChildrenAndAdd(cm.display.dragCursor, frag);
  }

  function clearDragCursor(cm) {
    if (cm.display.dragCursor) {
      cm.display.lineSpace.removeChild(cm.display.dragCursor);
      cm.display.dragCursor = null;
    }
  }

  // These must be handled carefully, because naively registering a
  // handler for each editor will cause the editors to never be
  // garbage collected.

  function forEachCodeMirror(f) {
    if (!document.getElementsByClassName) { return }
    var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
    for (var i = 0; i < byClass.length; i++) {
      var cm = byClass[i].CodeMirror;
      if (cm) { editors.push(cm); }
    }
    if (editors.length) { editors[0].operation(function () {
      for (var i = 0; i < editors.length; i++) { f(editors[i]); }
    }); }
  }

  var globalsRegistered = false;
  function ensureGlobalHandlers() {
    if (globalsRegistered) { return }
    registerGlobalHandlers();
    globalsRegistered = true;
  }
  function registerGlobalHandlers() {
    // When the window resizes, we need to refresh active editors.
    var resizeTimer;
    on(window, "resize", function () {
      if (resizeTimer == null) { resizeTimer = setTimeout(function () {
        resizeTimer = null;
        forEachCodeMirror(onResize);
      }, 100); }
    });
    // When the window loses focus, we want to show the editor as blurred
    on(window, "blur", function () { return forEachCodeMirror(onBlur); });
  }
  // Called when the window resizes
  function onResize(cm) {
    var d = cm.display;
    // Might be a text scaling operation, clear size caches.
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
    d.scrollbarsClipped = false;
    cm.setSize();
  }

  var keyNames = {
    3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
    19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
    36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
    46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
    106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
    173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
    221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
    63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
  };

  // Number keys
  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
  // Alphabetic keys
  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
  // Function keys
  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }

  var keyMap = {};

  keyMap.basic = {
    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
    "Esc": "singleSelection"
  };
  // Note that the save and find-related commands aren't defined by
  // default. User code or addons can define them. Unknown commands
  // are simply ignored.
  keyMap.pcDefault = {
    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
    "fallthrough": "basic"
  };
  // Very basic readline/emacs-style bindings, which are standard on Mac.
  keyMap.emacsy = {
    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
    "Ctrl-O": "openLine"
  };
  keyMap.macDefault = {
    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
    "fallthrough": ["basic", "emacsy"]
  };
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;

  // KEYMAP DISPATCH

  function normalizeKeyName(name) {
    var parts = name.split(/-(?!$)/);
    name = parts[parts.length - 1];
    var alt, ctrl, shift, cmd;
    for (var i = 0; i < parts.length - 1; i++) {
      var mod = parts[i];
      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
      else if (/^a(lt)?$/i.test(mod)) { alt = true; }
      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
      else if (/^s(hift)?$/i.test(mod)) { shift = true; }
      else { throw new Error("Unrecognized modifier name: " + mod) }
    }
    if (alt) { name = "Alt-" + name; }
    if (ctrl) { name = "Ctrl-" + name; }
    if (cmd) { name = "Cmd-" + name; }
    if (shift) { name = "Shift-" + name; }
    return name
  }

  // This is a kludge to keep keymaps mostly working as raw objects
  // (backwards compatibility) while at the same time support features
  // like normalization and multi-stroke key bindings. It compiles a
  // new normalized keymap, and then updates the old object to reflect
  // this.
  function normalizeKeyMap(keymap) {
    var copy = {};
    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
      var value = keymap[keyname];
      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
      if (value == "...") { delete keymap[keyname]; continue }

      var keys = map(keyname.split(" "), normalizeKeyName);
      for (var i = 0; i < keys.length; i++) {
        var val = (void 0), name = (void 0);
        if (i == keys.length - 1) {
          name = keys.join(" ");
          val = value;
        } else {
          name = keys.slice(0, i + 1).join(" ");
          val = "...";
        }
        var prev = copy[name];
        if (!prev) { copy[name] = val; }
        else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
      }
      delete keymap[keyname];
    } }
    for (var prop in copy) { keymap[prop] = copy[prop]; }
    return keymap
  }

  function lookupKey(key, map$$1, handle, context) {
    map$$1 = getKeyMap(map$$1);
    var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
    if (found === false) { return "nothing" }
    if (found === "...") { return "multi" }
    if (found != null && handle(found)) { return "handled" }

    if (map$$1.fallthrough) {
      if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
        { return lookupKey(key, map$$1.fallthrough, handle, context) }
      for (var i = 0; i < map$$1.fallthrough.length; i++) {
        var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
        if (result) { return result }
      }
    }
  }

  // Modifier key presses don't count as 'real' key presses for the
  // purpose of keymap fallthrough.
  function isModifierKey(value) {
    var name = typeof value == "string" ? value : keyNames[value.keyCode];
    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
  }

  function addModifierNames(name, event, noShift) {
    var base = name;
    if (event.altKey && base != "Alt") { name = "Alt-" + name; }
    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
    if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
    return name
  }

  // Look up the name of a key as indicated by an event object.
  function keyName(event, noShift) {
    if (presto && event.keyCode == 34 && event["char"]) { return false }
    var name = keyNames[event.keyCode];
    if (name == null || event.altGraphKey) { return false }
    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
    if (event.keyCode == 3 && event.code) { name = event.code; }
    return addModifierNames(name, event, noShift)
  }

  function getKeyMap(val) {
    return typeof val == "string" ? keyMap[val] : val
  }

  // Helper for deleting text near the selection(s), used to implement
  // backspace, delete, and similar functionality.
  function deleteNearSelection(cm, compute) {
    var ranges = cm.doc.sel.ranges, kill = [];
    // Build up a set of ranges to kill first, merging overlapping
    // ranges.
    for (var i = 0; i < ranges.length; i++) {
      var toKill = compute(ranges[i]);
      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
        var replaced = kill.pop();
        if (cmp(replaced.from, toKill.from) < 0) {
          toKill.from = replaced.from;
          break
        }
      }
      kill.push(toKill);
    }
    // Next, remove those actual ranges.
    runInOp(cm, function () {
      for (var i = kill.length - 1; i >= 0; i--)
        { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
      ensureCursorVisible(cm);
    });
  }

  function moveCharLogically(line, ch, dir) {
    var target = skipExtendingChars(line.text, ch + dir, dir);
    return target < 0 || target > line.text.length ? null : target
  }

  function moveLogically(line, start, dir) {
    var ch = moveCharLogically(line, start.ch, dir);
    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
  }

  function endOfLine(visually, cm, lineObj, lineNo, dir) {
    if (visually) {
      if (cm.doc.direction == "rtl") { dir = -dir; }
      var order = getOrder(lineObj, cm.doc.direction);
      if (order) {
        var part = dir < 0 ? lst(order) : order[0];
        var moveInStorageOrder = (dir < 0) == (part.level == 1);
        var sticky = moveInStorageOrder ? "after" : "before";
        var ch;
        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
        // it could be that the last bidi part is not on the last visual line,
        // since visual lines contain content order-consecutive chunks.
        // Thus, in rtl, we are looking for the first (content-order) character
        // in the rtl chunk that is on the last line (that is, the same line
        // as the last (content-order) character).
        if (part.level > 0 || cm.doc.direction == "rtl") {
          var prep = prepareMeasureForLine(cm, lineObj);
          ch = dir < 0 ? lineObj.text.length - 1 : 0;
          var targetTop = measureCharPrepared(cm, prep, ch).top;
          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
          if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
        } else { ch = dir < 0 ? part.to : part.from; }
        return new Pos(lineNo, ch, sticky)
      }
    }
    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
  }

  function moveVisually(cm, line, start, dir) {
    var bidi = getOrder(line, cm.doc.direction);
    if (!bidi) { return moveLogically(line, start, dir) }
    if (start.ch >= line.text.length) {
      start.ch = line.text.length;
      start.sticky = "before";
    } else if (start.ch <= 0) {
      start.ch = 0;
      start.sticky = "after";
    }
    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
    if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
      // nothing interesting happens.
      return moveLogically(line, start, dir)
    }

    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
    var prep;
    var getWrappedLineExtent = function (ch) {
      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
      prep = prep || prepareMeasureForLine(cm, line);
      return wrappedLineExtentChar(cm, line, prep, ch)
    };
    var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);

    if (cm.doc.direction == "rtl" || part.level == 1) {
      var moveInStorageOrder = (part.level == 1) == (dir < 0);
      var ch = mv(start, moveInStorageOrder ? 1 : -1);
      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
        // Case 2: We move within an rtl part or in an rtl editor on the same visual line
        var sticky = moveInStorageOrder ? "before" : "after";
        return new Pos(start.line, ch, sticky)
      }
    }

    // Case 3: Could not move within this bidi part in this visual line, so leave
    // the current bidi part

    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
        ? new Pos(start.line, mv(ch, 1), "before")
        : new Pos(start.line, ch, "after"); };

      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
        var part = bidi[partPos];
        var moveInStorageOrder = (dir > 0) == (part.level != 1);
        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
        ch = moveInStorageOrder ? part.from : mv(part.to, -1);
        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
      }
    };

    // Case 3a: Look for other bidi parts on the same visual line
    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
    if (res) { return res }

    // Case 3b: Look for other bidi parts on the next visual line
    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
      if (res) { return res }
    }

    // Case 4: Nowhere to move
    return null
  }

  // Commands are parameter-less actions that can be performed on an
  // editor, mostly used for keybindings.
  var commands = {
    selectAll: selectAll,
    singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
    killLine: function (cm) { return deleteNearSelection(cm, function (range) {
      if (range.empty()) {
        var len = getLine(cm.doc, range.head.line).text.length;
        if (range.head.ch == len && range.head.line < cm.lastLine())
          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
        else
          { return {from: range.head, to: Pos(range.head.line, len)} }
      } else {
        return {from: range.from(), to: range.to()}
      }
    }); },
    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
      from: Pos(range.from().line, 0),
      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
    }); }); },
    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
      from: Pos(range.from().line, 0), to: range.from()
    }); }); },
    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
      var top = cm.charCoords(range.head, "div").top + 5;
      var leftPos = cm.coordsChar({left: 0, top: top}, "div");
      return {from: leftPos, to: range.from()}
    }); },
    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
      var top = cm.charCoords(range.head, "div").top + 5;
      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
      return {from: range.from(), to: rightPos }
    }); },
    undo: function (cm) { return cm.undo(); },
    redo: function (cm) { return cm.redo(); },
    undoSelection: function (cm) { return cm.undoSelection(); },
    redoSelection: function (cm) { return cm.redoSelection(); },
    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
      {origin: "+move", bias: 1}
    ); },
    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
      {origin: "+move", bias: 1}
    ); },
    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
      {origin: "+move", bias: -1}
    ); },
    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
      var top = cm.cursorCoords(range.head, "div").top + 5;
      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
    }, sel_move); },
    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
      var top = cm.cursorCoords(range.head, "div").top + 5;
      return cm.coordsChar({left: 0, top: top}, "div")
    }, sel_move); },
    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
      var top = cm.cursorCoords(range.head, "div").top + 5;
      var pos = cm.coordsChar({left: 0, top: top}, "div");
      if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
      return pos
    }, sel_move); },
    goLineUp: function (cm) { return cm.moveV(-1, "line"); },
    goLineDown: function (cm) { return cm.moveV(1, "line"); },
    goPageUp: function (cm) { return cm.moveV(-1, "page"); },
    goPageDown: function (cm) { return cm.moveV(1, "page"); },
    goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
    goCharRight: function (cm) { return cm.moveH(1, "char"); },
    goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
    goColumnRight: function (cm) { return cm.moveH(1, "column"); },
    goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
    goGroupRight: function (cm) { return cm.moveH(1, "group"); },
    goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
    goWordRight: function (cm) { return cm.moveH(1, "word"); },
    delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
    delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
    delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
    delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
    delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
    delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
    indentAuto: function (cm) { return cm.indentSelection("smart"); },
    indentMore: function (cm) { return cm.indentSelection("add"); },
    indentLess: function (cm) { return cm.indentSelection("subtract"); },
    insertTab: function (cm) { return cm.replaceSelection("\t"); },
    insertSoftTab: function (cm) {
      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
      for (var i = 0; i < ranges.length; i++) {
        var pos = ranges[i].from();
        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
        spaces.push(spaceStr(tabSize - col % tabSize));
      }
      cm.replaceSelections(spaces);
    },
    defaultTab: function (cm) {
      if (cm.somethingSelected()) { cm.indentSelection("add"); }
      else { cm.execCommand("insertTab"); }
    },
    // Swap the two chars left and right of each selection's head.
    // Move cursor behind the two swapped characters afterwards.
    //
    // Doesn't consider line feeds a character.
    // Doesn't scan more than one line above to find a character.
    // Doesn't do anything on an empty line.
    // Doesn't do anything with non-empty selections.
    transposeChars: function (cm) { return runInOp(cm, function () {
      var ranges = cm.listSelections(), newSel = [];
      for (var i = 0; i < ranges.length; i++) {
        if (!ranges[i].empty()) { continue }
        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
        if (line) {
          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
          if (cur.ch > 0) {
            cur = new Pos(cur.line, cur.ch + 1);
            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
                            Pos(cur.line, cur.ch - 2), cur, "+transpose");
          } else if (cur.line > cm.doc.first) {
            var prev = getLine(cm.doc, cur.line - 1).text;
            if (prev) {
              cur = new Pos(cur.line, 1);
              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
                              prev.charAt(prev.length - 1),
                              Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
            }
          }
        }
        newSel.push(new Range(cur, cur));
      }
      cm.setSelections(newSel);
    }); },
    newlineAndIndent: function (cm) { return runInOp(cm, function () {
      var sels = cm.listSelections();
      for (var i = sels.length - 1; i >= 0; i--)
        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
      sels = cm.listSelections();
      for (var i$1 = 0; i$1 < sels.length; i$1++)
        { cm.indentLine(sels[i$1].from().line, null, true); }
      ensureCursorVisible(cm);
    }); },
    openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
  };


  function lineStart(cm, lineN) {
    var line = getLine(cm.doc, lineN);
    var visual = visualLine(line);
    if (visual != line) { lineN = lineNo(visual); }
    return endOfLine(true, cm, visual, lineN, 1)
  }
  function lineEnd(cm, lineN) {
    var line = getLine(cm.doc, lineN);
    var visual = visualLineEnd(line);
    if (visual != line) { lineN = lineNo(visual); }
    return endOfLine(true, cm, line, lineN, -1)
  }
  function lineStartSmart(cm, pos) {
    var start = lineStart(cm, pos.line);
    var line = getLine(cm.doc, start.line);
    var order = getOrder(line, cm.doc.direction);
    if (!order || order[0].level == 0) {
      var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
    }
    return start
  }

  // Run a handler that was bound to a key.
  function doHandleBinding(cm, bound, dropShift) {
    if (typeof bound == "string") {
      bound = commands[bound];
      if (!bound) { return false }
    }
    // Ensure previous input has been read, so that the handler sees a
    // consistent view of the document
    cm.display.input.ensurePolled();
    var prevShift = cm.display.shift, done = false;
    try {
      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
      if (dropShift) { cm.display.shift = false; }
      done = bound(cm) != Pass;
    } finally {
      cm.display.shift = prevShift;
      cm.state.suppressEdits = false;
    }
    return done
  }

  function lookupKeyForEditor(cm, name, handle) {
    for (var i = 0; i < cm.state.keyMaps.length; i++) {
      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
      if (result) { return result }
    }
    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
      || lookupKey(name, cm.options.keyMap, handle, cm)
  }

  // Note that, despite the name, this function is also used to check
  // for bound mouse clicks.

  var stopSeq = new Delayed;

  function dispatchKey(cm, name, e, handle) {
    var seq = cm.state.keySeq;
    if (seq) {
      if (isModifierKey(name)) { return "handled" }
      if (/\'$/.test(name))
        { cm.state.keySeq = null; }
      else
        { stopSeq.set(50, function () {
          if (cm.state.keySeq == seq) {
            cm.state.keySeq = null;
            cm.display.input.reset();
          }
        }); }
      if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
    }
    return dispatchKeyInner(cm, name, e, handle)
  }

  function dispatchKeyInner(cm, name, e, handle) {
    var result = lookupKeyForEditor(cm, name, handle);

    if (result == "multi")
      { cm.state.keySeq = name; }
    if (result == "handled")
      { signalLater(cm, "keyHandled", cm, name, e); }

    if (result == "handled" || result == "multi") {
      e_preventDefault(e);
      restartBlink(cm);
    }

    return !!result
  }

  // Handle a key from the keydown event.
  function handleKeyBinding(cm, e) {
    var name = keyName(e, true);
    if (!name) { return false }

    if (e.shiftKey && !cm.state.keySeq) {
      // First try to resolve full name (including 'Shift-'). Failing
      // that, see if there is a cursor-motion command (starting with
      // 'go') bound to the keyname without 'Shift-'.
      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
          || dispatchKey(cm, name, e, function (b) {
               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
                 { return doHandleBinding(cm, b) }
             })
    } else {
      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
    }
  }

  // Handle a key from the keypress event
  function handleCharBinding(cm, e, ch) {
    return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
  }

  var lastStoppedKey = null;
  function onKeyDown(e) {
    var cm = this;
    cm.curOp.focus = activeElt();
    if (signalDOMEvent(cm, e)) { return }
    // IE does strange things with escape.
    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
    var code = e.keyCode;
    cm.display.shift = code == 16 || e.shiftKey;
    var handled = handleKeyBinding(cm, e);
    if (presto) {
      lastStoppedKey = handled ? code : null;
      // Opera has no cut event... we try to at least catch the key combo
      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
        { cm.replaceSelection("", null, "cut"); }
    }
    if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)
      { document.execCommand("cut"); }

    // Turn mouse into crosshair when Alt is held on Mac.
    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
      { showCrossHair(cm); }
  }

  function showCrossHair(cm) {
    var lineDiv = cm.display.lineDiv;
    addClass(lineDiv, "CodeMirror-crosshair");

    function up(e) {
      if (e.keyCode == 18 || !e.altKey) {
        rmClass(lineDiv, "CodeMirror-crosshair");
        off(document, "keyup", up);
        off(document, "mouseover", up);
      }
    }
    on(document, "keyup", up);
    on(document, "mouseover", up);
  }

  function onKeyUp(e) {
    if (e.keyCode == 16) { this.doc.sel.shift = false; }
    signalDOMEvent(this, e);
  }

  function onKeyPress(e) {
    var cm = this;
    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
    var keyCode = e.keyCode, charCode = e.charCode;
    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
    // Some browsers fire keypress events for backspace
    if (ch == "\x08") { return }
    if (handleCharBinding(cm, e, ch)) { return }
    cm.display.input.onKeyPress(e);
  }

  var DOUBLECLICK_DELAY = 400;

  var PastClick = function(time, pos, button) {
    this.time = time;
    this.pos = pos;
    this.button = button;
  };

  PastClick.prototype.compare = function (time, pos, button) {
    return this.time + DOUBLECLICK_DELAY > time &&
      cmp(pos, this.pos) == 0 && button == this.button
  };

  var lastClick, lastDoubleClick;
  function clickRepeat(pos, button) {
    var now = +new Date;
    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
      lastClick = lastDoubleClick = null;
      return "triple"
    } else if (lastClick && lastClick.compare(now, pos, button)) {
      lastDoubleClick = new PastClick(now, pos, button);
      lastClick = null;
      return "double"
    } else {
      lastClick = new PastClick(now, pos, button);
      lastDoubleClick = null;
      return "single"
    }
  }

  // A mouse down can be a single click, double click, triple click,
  // start of selection drag, start of text drag, new cursor
  // (ctrl-click), rectangle drag (alt-drag), or xwin
  // middle-click-paste. Or it might be a click on something we should
  // not interfere with, such as a scrollbar or widget.
  function onMouseDown(e) {
    var cm = this, display = cm.display;
    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
    display.input.ensurePolled();
    display.shift = e.shiftKey;

    if (eventInWidget(display, e)) {
      if (!webkit) {
        // Briefly turn off draggability, to allow widgets to do
        // normal dragging things.
        display.scroller.draggable = false;
        setTimeout(function () { return display.scroller.draggable = true; }, 100);
      }
      return
    }
    if (clickInGutter(cm, e)) { return }
    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
    window.focus();

    // #3261: make sure, that we're not starting a second selection
    if (button == 1 && cm.state.selectingText)
      { cm.state.selectingText(e); }

    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }

    if (button == 1) {
      ensureFocus(cm);
      if (pos) { leftButtonDown(cm, pos, repeat, e); }
      else if (e_target(e) == display.scroller) { e_preventDefault(e); }
    } else if (button == 2) {
      if (pos) { extendSelection(cm.doc, pos); }
      setTimeout(function () { return display.input.focus(); }, 20);
    } else if (button == 3) {
      if (captureRightClick) { cm.display.input.onContextMenu(e); }
      else { delayBlurEvent(cm); }
    }
  }

  function handleMappedButton(cm, button, pos, repeat, event) {
    var name = "Click";
    if (repeat == "double") { name = "Double" + name; }
    else if (repeat == "triple") { name = "Triple" + name; }
    name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;

    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
      if (typeof bound == "string") { bound = commands[bound]; }
      if (!bound) { return false }
      var done = false;
      try {
        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
        done = bound(cm, pos) != Pass;
      } finally {
        cm.state.suppressEdits = false;
      }
      return done
    })
  }

  function configureMouse(cm, repeat, event) {
    var option = cm.getOption("configureMouse");
    var value = option ? option(cm, repeat, event) : {};
    if (value.unit == null) {
      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
      value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
    }
    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
    return value
  }

  function leftButtonDown(cm, pos, repeat, event) {
    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
    else { cm.curOp.focus = activeElt(); }

    var behavior = configureMouse(cm, repeat, event);

    var sel = cm.doc.sel, contained;
    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
        repeat == "single" && (contained = sel.contains(pos)) > -1 &&
        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
      { leftButtonStartDrag(cm, event, pos, behavior); }
    else
      { leftButtonSelect(cm, event, pos, behavior); }
  }

  // Start a text drag. When it ends, see if any dragging actually
  // happen, and treat as a click if it didn't.
  function leftButtonStartDrag(cm, event, pos, behavior) {
    var display = cm.display, moved = false;
    var dragEnd = operation(cm, function (e) {
      if (webkit) { display.scroller.draggable = false; }
      cm.state.draggingText = false;
      off(display.wrapper.ownerDocument, "mouseup", dragEnd);
      off(display.wrapper.ownerDocument, "mousemove", mouseMove);
      off(display.scroller, "dragstart", dragStart);
      off(display.scroller, "drop", dragEnd);
      if (!moved) {
        e_preventDefault(e);
        if (!behavior.addNew)
          { extendSelection(cm.doc, pos, null, null, behavior.extend); }
        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
        if (webkit || ie && ie_version == 9)
          { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
        else
          { display.input.focus(); }
      }
    });
    var mouseMove = function(e2) {
      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
    };
    var dragStart = function () { return moved = true; };
    // Let the drag handler handle this.
    if (webkit) { display.scroller.draggable = true; }
    cm.state.draggingText = dragEnd;
    dragEnd.copy = !behavior.moveOnDrag;
    // IE's approach to draggable
    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
    on(display.wrapper.ownerDocument, "mouseup", dragEnd);
    on(display.wrapper.ownerDocument, "mousemove", mouseMove);
    on(display.scroller, "dragstart", dragStart);
    on(display.scroller, "drop", dragEnd);

    delayBlurEvent(cm);
    setTimeout(function () { return display.input.focus(); }, 20);
  }

  function rangeForUnit(cm, pos, unit) {
    if (unit == "char") { return new Range(pos, pos) }
    if (unit == "word") { return cm.findWordAt(pos) }
    if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
    var result = unit(cm, pos);
    return new Range(result.from, result.to)
  }

  // Normal selection, as opposed to text dragging.
  function leftButtonSelect(cm, event, start, behavior) {
    var display = cm.display, doc = cm.doc;
    e_preventDefault(event);

    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
    if (behavior.addNew && !behavior.extend) {
      ourIndex = doc.sel.contains(start);
      if (ourIndex > -1)
        { ourRange = ranges[ourIndex]; }
      else
        { ourRange = new Range(start, start); }
    } else {
      ourRange = doc.sel.primary();
      ourIndex = doc.sel.primIndex;
    }

    if (behavior.unit == "rectangle") {
      if (!behavior.addNew) { ourRange = new Range(start, start); }
      start = posFromMouse(cm, event, true, true);
      ourIndex = -1;
    } else {
      var range$$1 = rangeForUnit(cm, start, behavior.unit);
      if (behavior.extend)
        { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
      else
        { ourRange = range$$1; }
    }

    if (!behavior.addNew) {
      ourIndex = 0;
      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
      startSel = doc.sel;
    } else if (ourIndex == -1) {
      ourIndex = ranges.length;
      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
                   {scroll: false, origin: "*mouse"});
    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
                   {scroll: false, origin: "*mouse"});
      startSel = doc.sel;
    } else {
      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
    }

    var lastPos = start;
    function extendTo(pos) {
      if (cmp(lastPos, pos) == 0) { return }
      lastPos = pos;

      if (behavior.unit == "rectangle") {
        var ranges = [], tabSize = cm.options.tabSize;
        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
             line <= end; line++) {
          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
          if (left == right)
            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
          else if (text.length > leftPos)
            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
        }
        if (!ranges.length) { ranges.push(new Range(start, start)); }
        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
                     {origin: "*mouse", scroll: false});
        cm.scrollIntoView(pos);
      } else {
        var oldRange = ourRange;
        var range$$1 = rangeForUnit(cm, pos, behavior.unit);
        var anchor = oldRange.anchor, head;
        if (cmp(range$$1.anchor, anchor) > 0) {
          head = range$$1.head;
          anchor = minPos(oldRange.from(), range$$1.anchor);
        } else {
          head = range$$1.anchor;
          anchor = maxPos(oldRange.to(), range$$1.head);
        }
        var ranges$1 = startSel.ranges.slice(0);
        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
      }
    }

    var editorSize = display.wrapper.getBoundingClientRect();
    // Used to ensure timeout re-tries don't fire when another extend
    // happened in the meantime (clearTimeout isn't reliable -- at
    // least on Chrome, the timeouts still happen even when cleared,
    // if the clear happens after their scheduled firing time).
    var counter = 0;

    function extend(e) {
      var curCount = ++counter;
      var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
      if (!cur) { return }
      if (cmp(cur, lastPos) != 0) {
        cm.curOp.focus = activeElt();
        extendTo(cur);
        var visible = visibleLines(display, doc);
        if (cur.line >= visible.to || cur.line < visible.from)
          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
      } else {
        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
        if (outside) { setTimeout(operation(cm, function () {
          if (counter != curCount) { return }
          display.scroller.scrollTop += outside;
          extend(e);
        }), 50); }
      }
    }

    function done(e) {
      cm.state.selectingText = false;
      counter = Infinity;
      // If e is null or undefined we interpret this as someone trying
      // to explicitly cancel the selection rather than the user
      // letting go of the mouse button.
      if (e) {
        e_preventDefault(e);
        display.input.focus();
      }
      off(display.wrapper.ownerDocument, "mousemove", move);
      off(display.wrapper.ownerDocument, "mouseup", up);
      doc.history.lastSelOrigin = null;
    }

    var move = operation(cm, function (e) {
      if (e.buttons === 0 || !e_button(e)) { done(e); }
      else { extend(e); }
    });
    var up = operation(cm, done);
    cm.state.selectingText = up;
    on(display.wrapper.ownerDocument, "mousemove", move);
    on(display.wrapper.ownerDocument, "mouseup", up);
  }

  // Used when mouse-selecting to adjust the anchor to the proper side
  // of a bidi jump depending on the visual position of the head.
  function bidiSimplify(cm, range$$1) {
    var anchor = range$$1.anchor;
    var head = range$$1.head;
    var anchorLine = getLine(cm.doc, anchor.line);
    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
    var order = getOrder(anchorLine);
    if (!order) { return range$$1 }
    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
    if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
    if (boundary == 0 || boundary == order.length) { return range$$1 }

    // Compute the relative visual position of the head compared to the
    // anchor (<0 is to the left, >0 to the right)
    var leftSide;
    if (head.line != anchor.line) {
      leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
    } else {
      var headIndex = getBidiPartAt(order, head.ch, head.sticky);
      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
      if (headIndex == boundary - 1 || headIndex == boundary)
        { leftSide = dir < 0; }
      else
        { leftSide = dir > 0; }
    }

    var usePart = order[boundary + (leftSide ? -1 : 0)];
    var from = leftSide == (usePart.level == 1);
    var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
    return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
  }


  // Determines whether an event happened in the gutter, and fires the
  // handlers for the corresponding event.
  function gutterEvent(cm, e, type, prevent) {
    var mX, mY;
    if (e.touches) {
      mX = e.touches[0].clientX;
      mY = e.touches[0].clientY;
    } else {
      try { mX = e.clientX; mY = e.clientY; }
      catch(e) { return false }
    }
    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
    if (prevent) { e_preventDefault(e); }

    var display = cm.display;
    var lineBox = display.lineDiv.getBoundingClientRect();

    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
    mY -= lineBox.top - display.viewOffset;

    for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
      var g = display.gutters.childNodes[i];
      if (g && g.getBoundingClientRect().right >= mX) {
        var line = lineAtHeight(cm.doc, mY);
        var gutter = cm.display.gutterSpecs[i];
        signal(cm, type, cm, line, gutter.className, e);
        return e_defaultPrevented(e)
      }
    }
  }

  function clickInGutter(cm, e) {
    return gutterEvent(cm, e, "gutterClick", true)
  }

  // CONTEXT MENU HANDLING

  // To make the context menu work, we need to briefly unhide the
  // textarea (making it as unobtrusive as possible) to let the
  // right-click take effect on it.
  function onContextMenu(cm, e) {
    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
    if (signalDOMEvent(cm, e, "contextmenu")) { return }
    if (!captureRightClick) { cm.display.input.onContextMenu(e); }
  }

  function contextMenuInGutter(cm, e) {
    if (!hasHandler(cm, "gutterContextMenu")) { return false }
    return gutterEvent(cm, e, "gutterContextMenu", false)
  }

  function themeChanged(cm) {
    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
    clearCaches(cm);
  }

  var Init = {toString: function(){return "CodeMirror.Init"}};

  var defaults = {};
  var optionHandlers = {};

  function defineOptions(CodeMirror) {
    var optionHandlers = CodeMirror.optionHandlers;

    function option(name, deflt, handle, notOnInit) {
      CodeMirror.defaults[name] = deflt;
      if (handle) { optionHandlers[name] =
        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
    }

    CodeMirror.defineOption = option;

    // Passed to option handlers when there is no old value.
    CodeMirror.Init = Init;

    // These two are, on init, called from the constructor because they
    // have to be initialized before the editor can start at all.
    option("value", "", function (cm, val) { return cm.setValue(val); }, true);
    option("mode", null, function (cm, val) {
      cm.doc.modeOption = val;
      loadMode(cm);
    }, true);

    option("indentUnit", 2, loadMode, true);
    option("indentWithTabs", false);
    option("smartIndent", true);
    option("tabSize", 4, function (cm) {
      resetModeState(cm);
      clearCaches(cm);
      regChange(cm);
    }, true);

    option("lineSeparator", null, function (cm, val) {
      cm.doc.lineSep = val;
      if (!val) { return }
      var newBreaks = [], lineNo = cm.doc.first;
      cm.doc.iter(function (line) {
        for (var pos = 0;;) {
          var found = line.text.indexOf(val, pos);
          if (found == -1) { break }
          pos = found + val.length;
          newBreaks.push(Pos(lineNo, found));
        }
        lineNo++;
      });
      for (var i = newBreaks.length - 1; i >= 0; i--)
        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
    });
    option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
      cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
      if (old != Init) { cm.refresh(); }
    });
    option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
    option("electricChars", true);
    option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
      throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
    }, true);
    option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
    option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
    option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
    option("rtlMoveVisually", !windows);
    option("wholeLineUpdateBefore", true);

    option("theme", "default", function (cm) {
      themeChanged(cm);
      updateGutters(cm);
    }, true);
    option("keyMap", "default", function (cm, val, old) {
      var next = getKeyMap(val);
      var prev = old != Init && getKeyMap(old);
      if (prev && prev.detach) { prev.detach(cm, next); }
      if (next.attach) { next.attach(cm, prev || null); }
    });
    option("extraKeys", null);
    option("configureMouse", null);

    option("lineWrapping", false, wrappingChanged, true);
    option("gutters", [], function (cm, val) {
      cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
      updateGutters(cm);
    }, true);
    option("fixedGutter", true, function (cm, val) {
      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
      cm.refresh();
    }, true);
    option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
    option("scrollbarStyle", "native", function (cm) {
      initScrollbars(cm);
      updateScrollbars(cm);
      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
    }, true);
    option("lineNumbers", false, function (cm, val) {
      cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
      updateGutters(cm);
    }, true);
    option("firstLineNumber", 1, updateGutters, true);
    option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
    option("showCursorWhenSelecting", false, updateSelection, true);

    option("resetSelectionOnContextMenu", true);
    option("lineWiseCopyCut", true);
    option("pasteLinesPerSelection", true);
    option("selectionsMayTouch", false);

    option("readOnly", false, function (cm, val) {
      if (val == "nocursor") {
        onBlur(cm);
        cm.display.input.blur();
      }
      cm.display.input.readOnlyChanged(val);
    });
    option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
    option("dragDrop", true, dragDropChanged);
    option("allowDropFileTypes", null);

    option("cursorBlinkRate", 530);
    option("cursorScrollMargin", 0);
    option("cursorHeight", 1, updateSelection, true);
    option("singleCursorHeightPerLine", true, updateSelection, true);
    option("workTime", 100);
    option("workDelay", 100);
    option("flattenSpans", true, resetModeState, true);
    option("addModeClass", false, resetModeState, true);
    option("pollInterval", 100);
    option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
    option("historyEventDelay", 1250);
    option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
    option("maxHighlightLength", 10000, resetModeState, true);
    option("moveInputWithCursor", true, function (cm, val) {
      if (!val) { cm.display.input.resetPosition(); }
    });

    option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
    option("autofocus", null);
    option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
    option("phrases", null);
  }

  function dragDropChanged(cm, value, old) {
    var wasOn = old && old != Init;
    if (!value != !wasOn) {
      var funcs = cm.display.dragFunctions;
      var toggle = value ? on : off;
      toggle(cm.display.scroller, "dragstart", funcs.start);
      toggle(cm.display.scroller, "dragenter", funcs.enter);
      toggle(cm.display.scroller, "dragover", funcs.over);
      toggle(cm.display.scroller, "dragleave", funcs.leave);
      toggle(cm.display.scroller, "drop", funcs.drop);
    }
  }

  function wrappingChanged(cm) {
    if (cm.options.lineWrapping) {
      addClass(cm.display.wrapper, "CodeMirror-wrap");
      cm.display.sizer.style.minWidth = "";
      cm.display.sizerWidth = null;
    } else {
      rmClass(cm.display.wrapper, "CodeMirror-wrap");
      findMaxLine(cm);
    }
    estimateLineHeights(cm);
    regChange(cm);
    clearCaches(cm);
    setTimeout(function () { return updateScrollbars(cm); }, 100);
  }

  // A CodeMirror instance represents an editor. This is the object
  // that user code is usually dealing with.

  function CodeMirror(place, options) {
    var this$1 = this;

    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }

    this.options = options = options ? copyObj(options) : {};
    // Determine effective options based on given values and defaults.
    copyObj(defaults, options, false);

    var doc = options.value;
    if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
    else if (options.mode) { doc.modeOption = options.mode; }
    this.doc = doc;

    var input = new CodeMirror.inputStyles[options.inputStyle](this);
    var display = this.display = new Display(place, doc, input, options);
    display.wrapper.CodeMirror = this;
    themeChanged(this);
    if (options.lineWrapping)
      { this.display.wrapper.className += " CodeMirror-wrap"; }
    initScrollbars(this);

    this.state = {
      keyMaps: [],  // stores maps added by addKeyMap
      overlays: [], // highlighting overlays, as added by addOverlay
      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
      overwrite: false,
      delayingBlurEvent: false,
      focused: false,
      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
      pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
      selectingText: false,
      draggingText: false,
      highlight: new Delayed(), // stores highlight worker timeout
      keySeq: null,  // Unfinished key sequence
      specialChars: null
    };

    if (options.autofocus && !mobile) { display.input.focus(); }

    // Override magic textarea content restore that IE sometimes does
    // on our hidden textarea on reload
    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }

    registerEventHandlers(this);
    ensureGlobalHandlers();

    startOperation(this);
    this.curOp.forceUpdate = true;
    attachDoc(this, doc);

    if ((options.autofocus && !mobile) || this.hasFocus())
      { setTimeout(bind(onFocus, this), 20); }
    else
      { onBlur(this); }

    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
      { optionHandlers[opt](this$1, options[opt], Init); } }
    maybeUpdateLineNumberWidth(this);
    if (options.finishInit) { options.finishInit(this); }
    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
    endOperation(this);
    // Suppress optimizelegibility in Webkit, since it breaks text
    // measuring on line wrapping boundaries.
    if (webkit && options.lineWrapping &&
        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
      { display.lineDiv.style.textRendering = "auto"; }
  }

  // The default configuration options.
  CodeMirror.defaults = defaults;
  // Functions to run when options are changed.
  CodeMirror.optionHandlers = optionHandlers;

  // Attach the necessary event handlers when initializing the editor
  function registerEventHandlers(cm) {
    var d = cm.display;
    on(d.scroller, "mousedown", operation(cm, onMouseDown));
    // Older IE's will not fire a second mousedown for a double click
    if (ie && ie_version < 11)
      { on(d.scroller, "dblclick", operation(cm, function (e) {
        if (signalDOMEvent(cm, e)) { return }
        var pos = posFromMouse(cm, e);
        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
        e_preventDefault(e);
        var word = cm.findWordAt(pos);
        extendSelection(cm.doc, word.anchor, word.head);
      })); }
    else
      { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
    // Some browsers fire contextmenu *after* opening the menu, at
    // which point we can't mess with it anymore. Context menu is
    // handled in onMouseDown for these browsers.
    on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
    on(d.input.getField(), "contextmenu", function (e) {
      if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }
    });

    // Used to suppress mouse event handling when a touch happens
    var touchFinished, prevTouch = {end: 0};
    function finishTouch() {
      if (d.activeTouch) {
        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
        prevTouch = d.activeTouch;
        prevTouch.end = +new Date;
      }
    }
    function isMouseLikeTouchEvent(e) {
      if (e.touches.length != 1) { return false }
      var touch = e.touches[0];
      return touch.radiusX <= 1 && touch.radiusY <= 1
    }
    function farAway(touch, other) {
      if (other.left == null) { return true }
      var dx = other.left - touch.left, dy = other.top - touch.top;
      return dx * dx + dy * dy > 20 * 20
    }
    on(d.scroller, "touchstart", function (e) {
      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
        d.input.ensurePolled();
        clearTimeout(touchFinished);
        var now = +new Date;
        d.activeTouch = {start: now, moved: false,
                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
        if (e.touches.length == 1) {
          d.activeTouch.left = e.touches[0].pageX;
          d.activeTouch.top = e.touches[0].pageY;
        }
      }
    });
    on(d.scroller, "touchmove", function () {
      if (d.activeTouch) { d.activeTouch.moved = true; }
    });
    on(d.scroller, "touchend", function (e) {
      var touch = d.activeTouch;
      if (touch && !eventInWidget(d, e) && touch.left != null &&
          !touch.moved && new Date - touch.start < 300) {
        var pos = cm.coordsChar(d.activeTouch, "page"), range;
        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
          { range = new Range(pos, pos); }
        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
          { range = cm.findWordAt(pos); }
        else // Triple tap
          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
        cm.setSelection(range.anchor, range.head);
        cm.focus();
        e_preventDefault(e);
      }
      finishTouch();
    });
    on(d.scroller, "touchcancel", finishTouch);

    // Sync scrolling between fake scrollbars and real scrollable
    // area, ensure viewport is updated when scrolling.
    on(d.scroller, "scroll", function () {
      if (d.scroller.clientHeight) {
        updateScrollTop(cm, d.scroller.scrollTop);
        setScrollLeft(cm, d.scroller.scrollLeft, true);
        signal(cm, "scroll", cm);
      }
    });

    // Listen to wheel events in order to try and update the viewport on time.
    on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
    on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });

    // Prevent wrapper from ever scrolling
    on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });

    d.dragFunctions = {
      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
      start: function (e) { return onDragStart(cm, e); },
      drop: operation(cm, onDrop),
      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
    };

    var inp = d.input.getField();
    on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
    on(inp, "keydown", operation(cm, onKeyDown));
    on(inp, "keypress", operation(cm, onKeyPress));
    on(inp, "focus", function (e) { return onFocus(cm, e); });
    on(inp, "blur", function (e) { return onBlur(cm, e); });
  }

  var initHooks = [];
  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };

  // Indent the given line. The how parameter can be "smart",
  // "add"/null, "subtract", or "prev". When aggressive is false
  // (typically set to true for forced single-line indents), empty
  // lines are not indented, and places where the mode returns Pass
  // are left alone.
  function indentLine(cm, n, how, aggressive) {
    var doc = cm.doc, state;
    if (how == null) { how = "add"; }
    if (how == "smart") {
      // Fall back to "prev" when the mode doesn't have an indentation
      // method.
      if (!doc.mode.indent) { how = "prev"; }
      else { state = getContextBefore(cm, n).state; }
    }

    var tabSize = cm.options.tabSize;
    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
    if (line.stateAfter) { line.stateAfter = null; }
    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
    if (!aggressive && !/\S/.test(line.text)) {
      indentation = 0;
      how = "not";
    } else if (how == "smart") {
      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
      if (indentation == Pass || indentation > 150) {
        if (!aggressive) { return }
        how = "prev";
      }
    }
    if (how == "prev") {
      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
      else { indentation = 0; }
    } else if (how == "add") {
      indentation = curSpace + cm.options.indentUnit;
    } else if (how == "subtract") {
      indentation = curSpace - cm.options.indentUnit;
    } else if (typeof how == "number") {
      indentation = curSpace + how;
    }
    indentation = Math.max(0, indentation);

    var indentString = "", pos = 0;
    if (cm.options.indentWithTabs)
      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
    if (pos < indentation) { indentString += spaceStr(indentation - pos); }

    if (indentString != curSpaceString) {
      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
      line.stateAfter = null;
      return true
    } else {
      // Ensure that, if the cursor was in the whitespace at the start
      // of the line, it is moved to the end of that space.
      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
        var range = doc.sel.ranges[i$1];
        if (range.head.line == n && range.head.ch < curSpaceString.length) {
          var pos$1 = Pos(n, curSpaceString.length);
          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
          break
        }
      }
    }
  }

  // This will be set to a {lineWise: bool, text: [string]} object, so
  // that, when pasting, we know what kind of selections the copied
  // text was made out of.
  var lastCopied = null;

  function setLastCopied(newLastCopied) {
    lastCopied = newLastCopied;
  }

  function applyTextInput(cm, inserted, deleted, sel, origin) {
    var doc = cm.doc;
    cm.display.shift = false;
    if (!sel) { sel = doc.sel; }

    var recent = +new Date - 200;
    var paste = origin == "paste" || cm.state.pasteIncoming > recent;
    var textLines = splitLinesAuto(inserted), multiPaste = null;
    // When pasting N lines into N selections, insert one line per selection
    if (paste && sel.ranges.length > 1) {
      if (lastCopied && lastCopied.text.join("\n") == inserted) {
        if (sel.ranges.length % lastCopied.text.length == 0) {
          multiPaste = [];
          for (var i = 0; i < lastCopied.text.length; i++)
            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
        }
      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
        multiPaste = map(textLines, function (l) { return [l]; });
      }
    }

    var updateInput = cm.curOp.updateInput;
    // Normal behavior is to insert the new text into every selection
    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
      var range$$1 = sel.ranges[i$1];
      var from = range$$1.from(), to = range$$1.to();
      if (range$$1.empty()) {
        if (deleted && deleted > 0) // Handle deletion
          { from = Pos(from.line, from.ch - deleted); }
        else if (cm.state.overwrite && !paste) // Handle overwrite
          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
          { from = to = Pos(from.line, 0); }
      }
      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
                         origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
      makeChange(cm.doc, changeEvent);
      signalLater(cm, "inputRead", cm, changeEvent);
    }
    if (inserted && !paste)
      { triggerElectric(cm, inserted); }

    ensureCursorVisible(cm);
    if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
    cm.curOp.typing = true;
    cm.state.pasteIncoming = cm.state.cutIncoming = -1;
  }

  function handlePaste(e, cm) {
    var pasted = e.clipboardData && e.clipboardData.getData("Text");
    if (pasted) {
      e.preventDefault();
      if (!cm.isReadOnly() && !cm.options.disableInput)
        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
      return true
    }
  }

  function triggerElectric(cm, inserted) {
    // When an 'electric' character is inserted, immediately trigger a reindent
    if (!cm.options.electricChars || !cm.options.smartIndent) { return }
    var sel = cm.doc.sel;

    for (var i = sel.ranges.length - 1; i >= 0; i--) {
      var range$$1 = sel.ranges[i];
      if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
      var mode = cm.getModeAt(range$$1.head);
      var indented = false;
      if (mode.electricChars) {
        for (var j = 0; j < mode.electricChars.length; j++)
          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
            indented = indentLine(cm, range$$1.head.line, "smart");
            break
          } }
      } else if (mode.electricInput) {
        if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
          { indented = indentLine(cm, range$$1.head.line, "smart"); }
      }
      if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
    }
  }

  function copyableRanges(cm) {
    var text = [], ranges = [];
    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
      var line = cm.doc.sel.ranges[i].head.line;
      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
      ranges.push(lineRange);
      text.push(cm.getRange(lineRange.anchor, lineRange.head));
    }
    return {text: text, ranges: ranges}
  }

  function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
    field.setAttribute("autocorrect", autocorrect ? "" : "off");
    field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
    field.setAttribute("spellcheck", !!spellcheck);
  }

  function hiddenTextarea() {
    var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
    // The textarea is kept positioned near the cursor to prevent the
    // fact that it'll be scrolled into view on input from scrolling
    // our fake cursor out of view. On webkit, when wrap=off, paste is
    // very slow. So make the area wide instead.
    if (webkit) { te.style.width = "1000px"; }
    else { te.setAttribute("wrap", "off"); }
    // If border: 0; -- iOS fails to open keyboard (issue #1287)
    if (ios) { te.style.border = "1px solid black"; }
    disableBrowserMagic(te);
    return div
  }

  // The publicly visible API. Note that methodOp(f) means
  // 'wrap f in an operation, performed on its `this` parameter'.

  // This is not the complete set of editor methods. Most of the
  // methods defined on the Doc type are also injected into
  // CodeMirror.prototype, for backwards compatibility and
  // convenience.

  function addEditorMethods(CodeMirror) {
    var optionHandlers = CodeMirror.optionHandlers;

    var helpers = CodeMirror.helpers = {};

    CodeMirror.prototype = {
      constructor: CodeMirror,
      focus: function(){window.focus(); this.display.input.focus();},

      setOption: function(option, value) {
        var options = this.options, old = options[option];
        if (options[option] == value && option != "mode") { return }
        options[option] = value;
        if (optionHandlers.hasOwnProperty(option))
          { operation(this, optionHandlers[option])(this, value, old); }
        signal(this, "optionChange", this, option);
      },

      getOption: function(option) {return this.options[option]},
      getDoc: function() {return this.doc},

      addKeyMap: function(map$$1, bottom) {
        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
      },
      removeKeyMap: function(map$$1) {
        var maps = this.state.keyMaps;
        for (var i = 0; i < maps.length; ++i)
          { if (maps[i] == map$$1 || maps[i].name == map$$1) {
            maps.splice(i, 1);
            return true
          } }
      },

      addOverlay: methodOp(function(spec, options) {
        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
        insertSorted(this.state.overlays,
                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
                      priority: (options && options.priority) || 0},
                     function (overlay) { return overlay.priority; });
        this.state.modeGen++;
        regChange(this);
      }),
      removeOverlay: methodOp(function(spec) {
        var this$1 = this;

        var overlays = this.state.overlays;
        for (var i = 0; i < overlays.length; ++i) {
          var cur = overlays[i].modeSpec;
          if (cur == spec || typeof spec == "string" && cur.name == spec) {
            overlays.splice(i, 1);
            this$1.state.modeGen++;
            regChange(this$1);
            return
          }
        }
      }),

      indentLine: methodOp(function(n, dir, aggressive) {
        if (typeof dir != "string" && typeof dir != "number") {
          if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
          else { dir = dir ? "add" : "subtract"; }
        }
        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
      }),
      indentSelection: methodOp(function(how) {
        var this$1 = this;

        var ranges = this.doc.sel.ranges, end = -1;
        for (var i = 0; i < ranges.length; i++) {
          var range$$1 = ranges[i];
          if (!range$$1.empty()) {
            var from = range$$1.from(), to = range$$1.to();
            var start = Math.max(end, from.line);
            end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
            for (var j = start; j < end; ++j)
              { indentLine(this$1, j, how); }
            var newRanges = this$1.doc.sel.ranges;
            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
              { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
          } else if (range$$1.head.line > end) {
            indentLine(this$1, range$$1.head.line, how, true);
            end = range$$1.head.line;
            if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
          }
        }
      }),

      // Fetch the parser token for a given character. Useful for hacks
      // that want to inspect the mode state (say, for completion).
      getTokenAt: function(pos, precise) {
        return takeToken(this, pos, precise)
      },

      getLineTokens: function(line, precise) {
        return takeToken(this, Pos(line), precise, true)
      },

      getTokenTypeAt: function(pos) {
        pos = clipPos(this.doc, pos);
        var styles = getLineStyles(this, getLine(this.doc, pos.line));
        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
        var type;
        if (ch == 0) { type = styles[2]; }
        else { for (;;) {
          var mid = (before + after) >> 1;
          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
          else { type = styles[mid * 2 + 2]; break }
        } }
        var cut = type ? type.indexOf("overlay ") : -1;
        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
      },

      getModeAt: function(pos) {
        var mode = this.doc.mode;
        if (!mode.innerMode) { return mode }
        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
      },

      getHelper: function(pos, type) {
        return this.getHelpers(pos, type)[0]
      },

      getHelpers: function(pos, type) {
        var this$1 = this;

        var found = [];
        if (!helpers.hasOwnProperty(type)) { return found }
        var help = helpers[type], mode = this.getModeAt(pos);
        if (typeof mode[type] == "string") {
          if (help[mode[type]]) { found.push(help[mode[type]]); }
        } else if (mode[type]) {
          for (var i = 0; i < mode[type].length; i++) {
            var val = help[mode[type][i]];
            if (val) { found.push(val); }
          }
        } else if (mode.helperType && help[mode.helperType]) {
          found.push(help[mode.helperType]);
        } else if (help[mode.name]) {
          found.push(help[mode.name]);
        }
        for (var i$1 = 0; i$1 < help._global.length; i$1++) {
          var cur = help._global[i$1];
          if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
            { found.push(cur.val); }
        }
        return found
      },

      getStateAfter: function(line, precise) {
        var doc = this.doc;
        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
        return getContextBefore(this, line + 1, precise).state
      },

      cursorCoords: function(start, mode) {
        var pos, range$$1 = this.doc.sel.primary();
        if (start == null) { pos = range$$1.head; }
        else if (typeof start == "object") { pos = clipPos(this.doc, start); }
        else { pos = start ? range$$1.from() : range$$1.to(); }
        return cursorCoords(this, pos, mode || "page")
      },

      charCoords: function(pos, mode) {
        return charCoords(this, clipPos(this.doc, pos), mode || "page")
      },

      coordsChar: function(coords, mode) {
        coords = fromCoordSystem(this, coords, mode || "page");
        return coordsChar(this, coords.left, coords.top)
      },

      lineAtHeight: function(height, mode) {
        height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
        return lineAtHeight(this.doc, height + this.display.viewOffset)
      },
      heightAtLine: function(line, mode, includeWidgets) {
        var end = false, lineObj;
        if (typeof line == "number") {
          var last = this.doc.first + this.doc.size - 1;
          if (line < this.doc.first) { line = this.doc.first; }
          else if (line > last) { line = last; end = true; }
          lineObj = getLine(this.doc, line);
        } else {
          lineObj = line;
        }
        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
          (end ? this.doc.height - heightAtLine(lineObj) : 0)
      },

      defaultTextHeight: function() { return textHeight(this.display) },
      defaultCharWidth: function() { return charWidth(this.display) },

      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},

      addWidget: function(pos, node, scroll, vert, horiz) {
        var display = this.display;
        pos = cursorCoords(this, clipPos(this.doc, pos));
        var top = pos.bottom, left = pos.left;
        node.style.position = "absolute";
        node.setAttribute("cm-ignore-events", "true");
        this.display.input.setUneditable(node);
        display.sizer.appendChild(node);
        if (vert == "over") {
          top = pos.top;
        } else if (vert == "above" || vert == "near") {
          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
          // Default to positioning above (if specified and possible); otherwise default to positioning below
          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
            { top = pos.top - node.offsetHeight; }
          else if (pos.bottom + node.offsetHeight <= vspace)
            { top = pos.bottom; }
          if (left + node.offsetWidth > hspace)
            { left = hspace - node.offsetWidth; }
        }
        node.style.top = top + "px";
        node.style.left = node.style.right = "";
        if (horiz == "right") {
          left = display.sizer.clientWidth - node.offsetWidth;
          node.style.right = "0px";
        } else {
          if (horiz == "left") { left = 0; }
          else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
          node.style.left = left + "px";
        }
        if (scroll)
          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
      },

      triggerOnKeyDown: methodOp(onKeyDown),
      triggerOnKeyPress: methodOp(onKeyPress),
      triggerOnKeyUp: onKeyUp,
      triggerOnMouseDown: methodOp(onMouseDown),

      execCommand: function(cmd) {
        if (commands.hasOwnProperty(cmd))
          { return commands[cmd].call(null, this) }
      },

      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),

      findPosH: function(from, amount, unit, visually) {
        var this$1 = this;

        var dir = 1;
        if (amount < 0) { dir = -1; amount = -amount; }
        var cur = clipPos(this.doc, from);
        for (var i = 0; i < amount; ++i) {
          cur = findPosH(this$1.doc, cur, dir, unit, visually);
          if (cur.hitSide) { break }
        }
        return cur
      },

      moveH: methodOp(function(dir, unit) {
        var this$1 = this;

        this.extendSelectionsBy(function (range$$1) {
          if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
            { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
          else
            { return dir < 0 ? range$$1.from() : range$$1.to() }
        }, sel_move);
      }),

      deleteH: methodOp(function(dir, unit) {
        var sel = this.doc.sel, doc = this.doc;
        if (sel.somethingSelected())
          { doc.replaceSelection("", null, "+delete"); }
        else
          { deleteNearSelection(this, function (range$$1) {
            var other = findPosH(doc, range$$1.head, dir, unit, false);
            return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
          }); }
      }),

      findPosV: function(from, amount, unit, goalColumn) {
        var this$1 = this;

        var dir = 1, x = goalColumn;
        if (amount < 0) { dir = -1; amount = -amount; }
        var cur = clipPos(this.doc, from);
        for (var i = 0; i < amount; ++i) {
          var coords = cursorCoords(this$1, cur, "div");
          if (x == null) { x = coords.left; }
          else { coords.left = x; }
          cur = findPosV(this$1, coords, dir, unit);
          if (cur.hitSide) { break }
        }
        return cur
      },

      moveV: methodOp(function(dir, unit) {
        var this$1 = this;

        var doc = this.doc, goals = [];
        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
        doc.extendSelectionsBy(function (range$$1) {
          if (collapse)
            { return dir < 0 ? range$$1.from() : range$$1.to() }
          var headPos = cursorCoords(this$1, range$$1.head, "div");
          if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
          goals.push(headPos.left);
          var pos = findPosV(this$1, headPos, dir, unit);
          if (unit == "page" && range$$1 == doc.sel.primary())
            { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
          return pos
        }, sel_move);
        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
          { doc.sel.ranges[i].goalColumn = goals[i]; } }
      }),

      // Find the word at the given position (as returned by coordsChar).
      findWordAt: function(pos) {
        var doc = this.doc, line = getLine(doc, pos.line).text;
        var start = pos.ch, end = pos.ch;
        if (line) {
          var helper = this.getHelper(pos, "wordChars");
          if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
          var startChar = line.charAt(start);
          var check = isWordChar(startChar, helper)
            ? function (ch) { return isWordChar(ch, helper); }
            : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
            : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
          while (start > 0 && check(line.charAt(start - 1))) { --start; }
          while (end < line.length && check(line.charAt(end))) { ++end; }
        }
        return new Range(Pos(pos.line, start), Pos(pos.line, end))
      },

      toggleOverwrite: function(value) {
        if (value != null && value == this.state.overwrite) { return }
        if (this.state.overwrite = !this.state.overwrite)
          { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
        else
          { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }

        signal(this, "overwriteToggle", this, this.state.overwrite);
      },
      hasFocus: function() { return this.display.input.getField() == activeElt() },
      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },

      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
      getScrollInfo: function() {
        var scroller = this.display.scroller;
        return {left: scroller.scrollLeft, top: scroller.scrollTop,
                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
      },

      scrollIntoView: methodOp(function(range$$1, margin) {
        if (range$$1 == null) {
          range$$1 = {from: this.doc.sel.primary().head, to: null};
          if (margin == null) { margin = this.options.cursorScrollMargin; }
        } else if (typeof range$$1 == "number") {
          range$$1 = {from: Pos(range$$1, 0), to: null};
        } else if (range$$1.from == null) {
          range$$1 = {from: range$$1, to: null};
        }
        if (!range$$1.to) { range$$1.to = range$$1.from; }
        range$$1.margin = margin || 0;

        if (range$$1.from.line != null) {
          scrollToRange(this, range$$1);
        } else {
          scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
        }
      }),

      setSize: methodOp(function(width, height) {
        var this$1 = this;

        var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
        if (width != null) { this.display.wrapper.style.width = interpret(width); }
        if (height != null) { this.display.wrapper.style.height = interpret(height); }
        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
        var lineNo$$1 = this.display.viewFrom;
        this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
          ++lineNo$$1;
        });
        this.curOp.forceUpdate = true;
        signal(this, "refresh", this);
      }),

      operation: function(f){return runInOp(this, f)},
      startOperation: function(){return startOperation(this)},
      endOperation: function(){return endOperation(this)},

      refresh: methodOp(function() {
        var oldHeight = this.display.cachedTextHeight;
        regChange(this);
        this.curOp.forceUpdate = true;
        clearCaches(this);
        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
        updateGutterSpace(this.display);
        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
          { estimateLineHeights(this); }
        signal(this, "refresh", this);
      }),

      swapDoc: methodOp(function(doc) {
        var old = this.doc;
        old.cm = null;
        // Cancel the current text selection if any (#5821)
        if (this.state.selectingText) { this.state.selectingText(); }
        attachDoc(this, doc);
        clearCaches(this);
        this.display.input.reset();
        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
        this.curOp.forceScroll = true;
        signalLater(this, "swapDoc", this, old);
        return old
      }),

      phrase: function(phraseText) {
        var phrases = this.options.phrases;
        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
      },

      getInputField: function(){return this.display.input.getField()},
      getWrapperElement: function(){return this.display.wrapper},
      getScrollerElement: function(){return this.display.scroller},
      getGutterElement: function(){return this.display.gutters}
    };
    eventMixin(CodeMirror);

    CodeMirror.registerHelper = function(type, name, value) {
      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
      helpers[type][name] = value;
    };
    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
      CodeMirror.registerHelper(type, name, value);
      helpers[type]._global.push({pred: predicate, val: value});
    };
  }

  // Used for horizontal relative motion. Dir is -1 or 1 (left or
  // right), unit can be "char", "column" (like char, but doesn't
  // cross line boundaries), "word" (across next word), or "group" (to
  // the start of next group of word or non-word-non-whitespace
  // chars). The visually param controls whether, in right-to-left
  // text, direction 1 means to move towards the next index in the
  // string, or towards the character to the right of the current
  // position. The resulting position will have a hitSide=true
  // property if it reached the end of the document.
  function findPosH(doc, pos, dir, unit, visually) {
    var oldPos = pos;
    var origDir = dir;
    var lineObj = getLine(doc, pos.line);
    var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
    function findNextLine() {
      var l = pos.line + lineDir;
      if (l < doc.first || l >= doc.first + doc.size) { return false }
      pos = new Pos(l, pos.ch, pos.sticky);
      return lineObj = getLine(doc, l)
    }
    function moveOnce(boundToLine) {
      var next;
      if (visually) {
        next = moveVisually(doc.cm, lineObj, pos, dir);
      } else {
        next = moveLogically(lineObj, pos, dir);
      }
      if (next == null) {
        if (!boundToLine && findNextLine())
          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }
        else
          { return false }
      } else {
        pos = next;
      }
      return true
    }

    if (unit == "char") {
      moveOnce();
    } else if (unit == "column") {
      moveOnce(true);
    } else if (unit == "word" || unit == "group") {
      var sawType = null, group = unit == "group";
      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
      for (var first = true;; first = false) {
        if (dir < 0 && !moveOnce(!first)) { break }
        var cur = lineObj.text.charAt(pos.ch) || "\n";
        var type = isWordChar(cur, helper) ? "w"
          : group && cur == "\n" ? "n"
          : !group || /\s/.test(cur) ? null
          : "p";
        if (group && !first && !type) { type = "s"; }
        if (sawType && sawType != type) {
          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
          break
        }

        if (type) { sawType = type; }
        if (dir > 0 && !moveOnce(!first)) { break }
      }
    }
    var result = skipAtomic(doc, pos, oldPos, origDir, true);
    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
    return result
  }

  // For relative vertical movement. Dir may be -1 or 1. Unit can be
  // "page" or "line". The resulting position will have a hitSide=true
  // property if it reached the end of the document.
  function findPosV(cm, pos, dir, unit) {
    var doc = cm.doc, x = pos.left, y;
    if (unit == "page") {
      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;

    } else if (unit == "line") {
      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
    }
    var target;
    for (;;) {
      target = coordsChar(cm, x, y);
      if (!target.outside) { break }
      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
      y += dir * 5;
    }
    return target
  }

  // CONTENTEDITABLE INPUT STYLE

  var ContentEditableInput = function(cm) {
    this.cm = cm;
    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
    this.polling = new Delayed();
    this.composing = null;
    this.gracePeriod = false;
    this.readDOMTimeout = null;
  };

  ContentEditableInput.prototype.init = function (display) {
      var this$1 = this;

    var input = this, cm = input.cm;
    var div = input.div = display.lineDiv;
    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);

    on(div, "paste", function (e) {
      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
      // IE doesn't fire input events, so we schedule a read for the pasted content in this way
      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
    });

    on(div, "compositionstart", function (e) {
      this$1.composing = {data: e.data, done: false};
    });
    on(div, "compositionupdate", function (e) {
      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
    });
    on(div, "compositionend", function (e) {
      if (this$1.composing) {
        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
        this$1.composing.done = true;
      }
    });

    on(div, "touchstart", function () { return input.forceCompositionEnd(); });

    on(div, "input", function () {
      if (!this$1.composing) { this$1.readFromDOMSoon(); }
    });

    function onCopyCut(e) {
      if (signalDOMEvent(cm, e)) { return }
      if (cm.somethingSelected()) {
        setLastCopied({lineWise: false, text: cm.getSelections()});
        if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
      } else if (!cm.options.lineWiseCopyCut) {
        return
      } else {
        var ranges = copyableRanges(cm);
        setLastCopied({lineWise: true, text: ranges.text});
        if (e.type == "cut") {
          cm.operation(function () {
            cm.setSelections(ranges.ranges, 0, sel_dontScroll);
            cm.replaceSelection("", null, "cut");
          });
        }
      }
      if (e.clipboardData) {
        e.clipboardData.clearData();
        var content = lastCopied.text.join("\n");
        // iOS exposes the clipboard API, but seems to discard content inserted into it
        e.clipboardData.setData("Text", content);
        if (e.clipboardData.getData("Text") == content) {
          e.preventDefault();
          return
        }
      }
      // Old-fashioned briefly-focus-a-textarea hack
      var kludge = hiddenTextarea(), te = kludge.firstChild;
      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
      te.value = lastCopied.text.join("\n");
      var hadFocus = document.activeElement;
      selectInput(te);
      setTimeout(function () {
        cm.display.lineSpace.removeChild(kludge);
        hadFocus.focus();
        if (hadFocus == div) { input.showPrimarySelection(); }
      }, 50);
    }
    on(div, "copy", onCopyCut);
    on(div, "cut", onCopyCut);
  };

  ContentEditableInput.prototype.prepareSelection = function () {
    var result = prepareSelection(this.cm, false);
    result.focus = this.cm.state.focused;
    return result
  };

  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
    if (!info || !this.cm.display.view.length) { return }
    if (info.focus || takeFocus) { this.showPrimarySelection(); }
    this.showMultipleSelections(info);
  };

  ContentEditableInput.prototype.getSelection = function () {
    return this.cm.display.wrapper.ownerDocument.getSelection()
  };

  ContentEditableInput.prototype.showPrimarySelection = function () {
    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
    var from = prim.from(), to = prim.to();

    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
      sel.removeAllRanges();
      return
    }

    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
        cmp(minPos(curAnchor, curFocus), from) == 0 &&
        cmp(maxPos(curAnchor, curFocus), to) == 0)
      { return }

    var view = cm.display.view;
    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
        {node: view[0].measure.map[2], offset: 0};
    var end = to.line < cm.display.viewTo && posToDOM(cm, to);
    if (!end) {
      var measure = view[view.length - 1].measure;
      var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
      end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
    }

    if (!start || !end) {
      sel.removeAllRanges();
      return
    }

    var old = sel.rangeCount && sel.getRangeAt(0), rng;
    try { rng = range(start.node, start.offset, end.offset, end.node); }
    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
    if (rng) {
      if (!gecko && cm.state.focused) {
        sel.collapse(start.node, start.offset);
        if (!rng.collapsed) {
          sel.removeAllRanges();
          sel.addRange(rng);
        }
      } else {
        sel.removeAllRanges();
        sel.addRange(rng);
      }
      if (old && sel.anchorNode == null) { sel.addRange(old); }
      else if (gecko) { this.startGracePeriod(); }
    }
    this.rememberSelection();
  };

  ContentEditableInput.prototype.startGracePeriod = function () {
      var this$1 = this;

    clearTimeout(this.gracePeriod);
    this.gracePeriod = setTimeout(function () {
      this$1.gracePeriod = false;
      if (this$1.selectionChanged())
        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
    }, 20);
  };

  ContentEditableInput.prototype.showMultipleSelections = function (info) {
    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
  };

  ContentEditableInput.prototype.rememberSelection = function () {
    var sel = this.getSelection();
    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
  };

  ContentEditableInput.prototype.selectionInEditor = function () {
    var sel = this.getSelection();
    if (!sel.rangeCount) { return false }
    var node = sel.getRangeAt(0).commonAncestorContainer;
    return contains(this.div, node)
  };

  ContentEditableInput.prototype.focus = function () {
    if (this.cm.options.readOnly != "nocursor") {
      if (!this.selectionInEditor())
        { this.showSelection(this.prepareSelection(), true); }
      this.div.focus();
    }
  };
  ContentEditableInput.prototype.blur = function () { this.div.blur(); };
  ContentEditableInput.prototype.getField = function () { return this.div };

  ContentEditableInput.prototype.supportsTouch = function () { return true };

  ContentEditableInput.prototype.receivedFocus = function () {
    var input = this;
    if (this.selectionInEditor())
      { this.pollSelection(); }
    else
      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }

    function poll() {
      if (input.cm.state.focused) {
        input.pollSelection();
        input.polling.set(input.cm.options.pollInterval, poll);
      }
    }
    this.polling.set(this.cm.options.pollInterval, poll);
  };

  ContentEditableInput.prototype.selectionChanged = function () {
    var sel = this.getSelection();
    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
  };

  ContentEditableInput.prototype.pollSelection = function () {
    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
    var sel = this.getSelection(), cm = this.cm;
    // On Android Chrome (version 56, at least), backspacing into an
    // uneditable block element will put the cursor in that element,
    // and then, because it's not editable, hide the virtual keyboard.
    // Because Android doesn't allow us to actually detect backspace
    // presses in a sane way, this code checks for when that happens
    // and simulates a backspace press in this case.
    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
      this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
      this.blur();
      this.focus();
      return
    }
    if (this.composing) { return }
    this.rememberSelection();
    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
    var head = domToPos(cm, sel.focusNode, sel.focusOffset);
    if (anchor && head) { runInOp(cm, function () {
      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
    }); }
  };

  ContentEditableInput.prototype.pollContent = function () {
    if (this.readDOMTimeout != null) {
      clearTimeout(this.readDOMTimeout);
      this.readDOMTimeout = null;
    }

    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
    var from = sel.from(), to = sel.to();
    if (from.ch == 0 && from.line > cm.firstLine())
      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
      { to = Pos(to.line + 1, 0); }
    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }

    var fromIndex, fromLine, fromNode;
    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
      fromLine = lineNo(display.view[0].line);
      fromNode = display.view[0].node;
    } else {
      fromLine = lineNo(display.view[fromIndex].line);
      fromNode = display.view[fromIndex - 1].node.nextSibling;
    }
    var toIndex = findViewIndex(cm, to.line);
    var toLine, toNode;
    if (toIndex == display.view.length - 1) {
      toLine = display.viewTo - 1;
      toNode = display.lineDiv.lastChild;
    } else {
      toLine = lineNo(display.view[toIndex + 1].line) - 1;
      toNode = display.view[toIndex + 1].node.previousSibling;
    }

    if (!fromNode) { return false }
    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
    while (newText.length > 1 && oldText.length > 1) {
      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
      else { break }
    }

    var cutFront = 0, cutEnd = 0;
    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
      { ++cutFront; }
    var newBot = lst(newText), oldBot = lst(oldText);
    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
                             oldBot.length - (oldText.length == 1 ? cutFront : 0));
    while (cutEnd < maxCutEnd &&
           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
      { ++cutEnd; }
    // Try to move start of change to start of selection if ambiguous
    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
      while (cutFront && cutFront > from.ch &&
             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
        cutFront--;
        cutEnd++;
      }
    }

    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
    newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");

    var chFrom = Pos(fromLine, cutFront);
    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
      replaceRange(cm.doc, newText, chFrom, chTo, "+input");
      return true
    }
  };

  ContentEditableInput.prototype.ensurePolled = function () {
    this.forceCompositionEnd();
  };
  ContentEditableInput.prototype.reset = function () {
    this.forceCompositionEnd();
  };
  ContentEditableInput.prototype.forceCompositionEnd = function () {
    if (!this.composing) { return }
    clearTimeout(this.readDOMTimeout);
    this.composing = null;
    this.updateFromDOM();
    this.div.blur();
    this.div.focus();
  };
  ContentEditableInput.prototype.readFromDOMSoon = function () {
      var this$1 = this;

    if (this.readDOMTimeout != null) { return }
    this.readDOMTimeout = setTimeout(function () {
      this$1.readDOMTimeout = null;
      if (this$1.composing) {
        if (this$1.composing.done) { this$1.composing = null; }
        else { return }
      }
      this$1.updateFromDOM();
    }, 80);
  };

  ContentEditableInput.prototype.updateFromDOM = function () {
      var this$1 = this;

    if (this.cm.isReadOnly() || !this.pollContent())
      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
  };

  ContentEditableInput.prototype.setUneditable = function (node) {
    node.contentEditable = "false";
  };

  ContentEditableInput.prototype.onKeyPress = function (e) {
    if (e.charCode == 0 || this.composing) { return }
    e.preventDefault();
    if (!this.cm.isReadOnly())
      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
  };

  ContentEditableInput.prototype.readOnlyChanged = function (val) {
    this.div.contentEditable = String(val != "nocursor");
  };

  ContentEditableInput.prototype.onContextMenu = function () {};
  ContentEditableInput.prototype.resetPosition = function () {};

  ContentEditableInput.prototype.needsContentAttribute = true;

  function posToDOM(cm, pos) {
    var view = findViewForLine(cm, pos.line);
    if (!view || view.hidden) { return null }
    var line = getLine(cm.doc, pos.line);
    var info = mapFromLineView(view, line, pos.line);

    var order = getOrder(line, cm.doc.direction), side = "left";
    if (order) {
      var partPos = getBidiPartAt(order, pos.ch);
      side = partPos % 2 ? "right" : "left";
    }
    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
    result.offset = result.collapse == "right" ? result.end : result.start;
    return result
  }

  function isInGutter(node) {
    for (var scan = node; scan; scan = scan.parentNode)
      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
    return false
  }

  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }

  function domTextBetween(cm, from, to, fromLine, toLine) {
    var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
    function close() {
      if (closing) {
        text += lineSep;
        if (extraLinebreak) { text += lineSep; }
        closing = extraLinebreak = false;
      }
    }
    function addText(str) {
      if (str) {
        close();
        text += str;
      }
    }
    function walk(node) {
      if (node.nodeType == 1) {
        var cmText = node.getAttribute("cm-text");
        if (cmText) {
          addText(cmText);
          return
        }
        var markerID = node.getAttribute("cm-marker"), range$$1;
        if (markerID) {
          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
          if (found.length && (range$$1 = found[0].find(0)))
            { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
          return
        }
        if (node.getAttribute("contenteditable") == "false") { return }
        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }

        if (isBlock) { close(); }
        for (var i = 0; i < node.childNodes.length; i++)
          { walk(node.childNodes[i]); }

        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
        if (isBlock) { closing = true; }
      } else if (node.nodeType == 3) {
        addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
      }
    }
    for (;;) {
      walk(from);
      if (from == to) { break }
      from = from.nextSibling;
      extraLinebreak = false;
    }
    return text
  }

  function domToPos(cm, node, offset) {
    var lineNode;
    if (node == cm.display.lineDiv) {
      lineNode = cm.display.lineDiv.childNodes[offset];
      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
      node = null; offset = 0;
    } else {
      for (lineNode = node;; lineNode = lineNode.parentNode) {
        if (!lineNode || lineNode == cm.display.lineDiv) { return null }
        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
      }
    }
    for (var i = 0; i < cm.display.view.length; i++) {
      var lineView = cm.display.view[i];
      if (lineView.node == lineNode)
        { return locateNodeInLineView(lineView, node, offset) }
    }
  }

  function locateNodeInLineView(lineView, node, offset) {
    var wrapper = lineView.text.firstChild, bad = false;
    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
    if (node == wrapper) {
      bad = true;
      node = wrapper.childNodes[offset];
      offset = 0;
      if (!node) {
        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
        return badPos(Pos(lineNo(line), line.text.length), bad)
      }
    }

    var textNode = node.nodeType == 3 ? node : null, topNode = node;
    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
      textNode = node.firstChild;
      if (offset) { offset = textNode.nodeValue.length; }
    }
    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
    var measure = lineView.measure, maps = measure.maps;

    function find(textNode, topNode, offset) {
      for (var i = -1; i < (maps ? maps.length : 0); i++) {
        var map$$1 = i < 0 ? measure.map : maps[i];
        for (var j = 0; j < map$$1.length; j += 3) {
          var curNode = map$$1[j + 2];
          if (curNode == textNode || curNode == topNode) {
            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
            var ch = map$$1[j] + offset;
            if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
            return Pos(line, ch)
          }
        }
      }
    }
    var found = find(textNode, topNode, offset);
    if (found) { return badPos(found, bad) }

    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
      found = find(after, after.firstChild, 0);
      if (found)
        { return badPos(Pos(found.line, found.ch - dist), bad) }
      else
        { dist += after.textContent.length; }
    }
    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
      found = find(before, before.firstChild, -1);
      if (found)
        { return badPos(Pos(found.line, found.ch + dist$1), bad) }
      else
        { dist$1 += before.textContent.length; }
    }
  }

  // TEXTAREA INPUT STYLE

  var TextareaInput = function(cm) {
    this.cm = cm;
    // See input.poll and input.reset
    this.prevInput = "";

    // Flag that indicates whether we expect input to appear real soon
    // now (after some event like 'keypress' or 'input') and are
    // polling intensively.
    this.pollingFast = false;
    // Self-resetting timeout for the poller
    this.polling = new Delayed();
    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
    this.hasSelection = false;
    this.composing = null;
  };

  TextareaInput.prototype.init = function (display) {
      var this$1 = this;

    var input = this, cm = this.cm;
    this.createField(display);
    var te = this.textarea;

    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);

    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
    if (ios) { te.style.width = "0px"; }

    on(te, "input", function () {
      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
      input.poll();
    });

    on(te, "paste", function (e) {
      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }

      cm.state.pasteIncoming = +new Date;
      input.fastPoll();
    });

    function prepareCopyCut(e) {
      if (signalDOMEvent(cm, e)) { return }
      if (cm.somethingSelected()) {
        setLastCopied({lineWise: false, text: cm.getSelections()});
      } else if (!cm.options.lineWiseCopyCut) {
        return
      } else {
        var ranges = copyableRanges(cm);
        setLastCopied({lineWise: true, text: ranges.text});
        if (e.type == "cut") {
          cm.setSelections(ranges.ranges, null, sel_dontScroll);
        } else {
          input.prevInput = "";
          te.value = ranges.text.join("\n");
          selectInput(te);
        }
      }
      if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
    }
    on(te, "cut", prepareCopyCut);
    on(te, "copy", prepareCopyCut);

    on(display.scroller, "paste", function (e) {
      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
      if (!te.dispatchEvent) {
        cm.state.pasteIncoming = +new Date;
        input.focus();
        return
      }

      // Pass the `paste` event to the textarea so it's handled by its event listener.
      var event = new Event("paste");
      event.clipboardData = e.clipboardData;
      te.dispatchEvent(event);
    });

    // Prevent normal selection in the editor (we handle our own)
    on(display.lineSpace, "selectstart", function (e) {
      if (!eventInWidget(display, e)) { e_preventDefault(e); }
    });

    on(te, "compositionstart", function () {
      var start = cm.getCursor("from");
      if (input.composing) { input.composing.range.clear(); }
      input.composing = {
        start: start,
        range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
      };
    });
    on(te, "compositionend", function () {
      if (input.composing) {
        input.poll();
        input.composing.range.clear();
        input.composing = null;
      }
    });
  };

  TextareaInput.prototype.createField = function (_display) {
    // Wraps and hides input textarea
    this.wrapper = hiddenTextarea();
    // The semihidden textarea that is focused when the editor is
    // focused, and receives input.
    this.textarea = this.wrapper.firstChild;
  };

  TextareaInput.prototype.prepareSelection = function () {
    // Redraw the selection and/or cursor
    var cm = this.cm, display = cm.display, doc = cm.doc;
    var result = prepareSelection(cm);

    // Move the hidden textarea near the cursor to prevent scrolling artifacts
    if (cm.options.moveInputWithCursor) {
      var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
                                          headPos.top + lineOff.top - wrapOff.top));
      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
                                           headPos.left + lineOff.left - wrapOff.left));
    }

    return result
  };

  TextareaInput.prototype.showSelection = function (drawn) {
    var cm = this.cm, display = cm.display;
    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
    removeChildrenAndAdd(display.selectionDiv, drawn.selection);
    if (drawn.teTop != null) {
      this.wrapper.style.top = drawn.teTop + "px";
      this.wrapper.style.left = drawn.teLeft + "px";
    }
  };

  // Reset the input to correspond to the selection (or to be empty,
  // when not typing and nothing is selected)
  TextareaInput.prototype.reset = function (typing) {
    if (this.contextMenuPending || this.composing) { return }
    var cm = this.cm;
    if (cm.somethingSelected()) {
      this.prevInput = "";
      var content = cm.getSelection();
      this.textarea.value = content;
      if (cm.state.focused) { selectInput(this.textarea); }
      if (ie && ie_version >= 9) { this.hasSelection = content; }
    } else if (!typing) {
      this.prevInput = this.textarea.value = "";
      if (ie && ie_version >= 9) { this.hasSelection = null; }
    }
  };

  TextareaInput.prototype.getField = function () { return this.textarea };

  TextareaInput.prototype.supportsTouch = function () { return false };

  TextareaInput.prototype.focus = function () {
    if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
      try { this.textarea.focus(); }
      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
    }
  };

  TextareaInput.prototype.blur = function () { this.textarea.blur(); };

  TextareaInput.prototype.resetPosition = function () {
    this.wrapper.style.top = this.wrapper.style.left = 0;
  };

  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };

  // Poll for input changes, using the normal rate of polling. This
  // runs as long as the editor is focused.
  TextareaInput.prototype.slowPoll = function () {
      var this$1 = this;

    if (this.pollingFast) { return }
    this.polling.set(this.cm.options.pollInterval, function () {
      this$1.poll();
      if (this$1.cm.state.focused) { this$1.slowPoll(); }
    });
  };

  // When an event has just come in that is likely to add or change
  // something in the input textarea, we poll faster, to ensure that
  // the change appears on the screen quickly.
  TextareaInput.prototype.fastPoll = function () {
    var missed = false, input = this;
    input.pollingFast = true;
    function p() {
      var changed = input.poll();
      if (!changed && !missed) {missed = true; input.polling.set(60, p);}
      else {input.pollingFast = false; input.slowPoll();}
    }
    input.polling.set(20, p);
  };

  // Read input from the textarea, and update the document to match.
  // When something is selected, it is present in the textarea, and
  // selected (unless it is huge, in which case a placeholder is
  // used). When nothing is selected, the cursor sits after previously
  // seen text (can be empty), which is stored in prevInput (we must
  // not reset the textarea when typing, because that breaks IME).
  TextareaInput.prototype.poll = function () {
      var this$1 = this;

    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
    // Since this is called a *lot*, try to bail out as cheaply as
    // possible when it is clear that nothing happened. hasSelection
    // will be the case when there is a lot of text in the textarea,
    // in which case reading its value would be expensive.
    if (this.contextMenuPending || !cm.state.focused ||
        (hasSelection(input) && !prevInput && !this.composing) ||
        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
      { return false }

    var text = input.value;
    // If nothing changed, bail.
    if (text == prevInput && !cm.somethingSelected()) { return false }
    // Work around nonsensical selection resetting in IE9/10, and
    // inexplicable appearance of private area unicode characters on
    // some key combos in Mac (#2689).
    if (ie && ie_version >= 9 && this.hasSelection === text ||
        mac && /[\uf700-\uf7ff]/.test(text)) {
      cm.display.input.reset();
      return false
    }

    if (cm.doc.sel == cm.display.selForContextMenu) {
      var first = text.charCodeAt(0);
      if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
      if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
    }
    // Find the part of the input that is actually new
    var same = 0, l = Math.min(prevInput.length, text.length);
    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }

    runInOp(cm, function () {
      applyTextInput(cm, text.slice(same), prevInput.length - same,
                     null, this$1.composing ? "*compose" : null);

      // Don't leave long text in the textarea, since it makes further polling slow
      if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
      else { this$1.prevInput = text; }

      if (this$1.composing) {
        this$1.composing.range.clear();
        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
                                           {className: "CodeMirror-composing"});
      }
    });
    return true
  };

  TextareaInput.prototype.ensurePolled = function () {
    if (this.pollingFast && this.poll()) { this.pollingFast = false; }
  };

  TextareaInput.prototype.onKeyPress = function () {
    if (ie && ie_version >= 9) { this.hasSelection = null; }
    this.fastPoll();
  };

  TextareaInput.prototype.onContextMenu = function (e) {
    var input = this, cm = input.cm, display = cm.display, te = input.textarea;
    if (input.contextMenuPending) { input.contextMenuPending(); }
    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
    if (!pos || presto) { return } // Opera is difficult.

    // Reset the current text selection only if the click is done outside of the selection
    // and 'resetSelectionOnContextMenu' option is true.
    var reset = cm.options.resetSelectionOnContextMenu;
    if (reset && cm.doc.sel.contains(pos) == -1)
      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }

    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
    var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
    input.wrapper.style.cssText = "position: static";
    te.style.cssText = "position: absolute; width: 30px; height: 30px;\n      top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n      z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
    var oldScrollY;
    if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
    display.input.focus();
    if (webkit) { window.scrollTo(null, oldScrollY); }
    display.input.reset();
    // Adds "Select all" to context menu in FF
    if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
    input.contextMenuPending = rehide;
    display.selForContextMenu = cm.doc.sel;
    clearTimeout(display.detectingSelectAll);

    // Select-all will be greyed out if there's nothing to select, so
    // this adds a zero-width space so that we can later check whether
    // it got selected.
    function prepareSelectAllHack() {
      if (te.selectionStart != null) {
        var selected = cm.somethingSelected();
        var extval = "\u200b" + (selected ? te.value : "");
        te.value = "\u21da"; // Used to catch context-menu undo
        te.value = extval;
        input.prevInput = selected ? "" : "\u200b";
        te.selectionStart = 1; te.selectionEnd = extval.length;
        // Re-set this, in case some other handler touched the
        // selection in the meantime.
        display.selForContextMenu = cm.doc.sel;
      }
    }
    function rehide() {
      if (input.contextMenuPending != rehide) { return }
      input.contextMenuPending = false;
      input.wrapper.style.cssText = oldWrapperCSS;
      te.style.cssText = oldCSS;
      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }

      // Try to detect the user choosing select-all
      if (te.selectionStart != null) {
        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
        var i = 0, poll = function () {
          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
              te.selectionEnd > 0 && input.prevInput == "\u200b") {
            operation(cm, selectAll)(cm);
          } else if (i++ < 10) {
            display.detectingSelectAll = setTimeout(poll, 500);
          } else {
            display.selForContextMenu = null;
            display.input.reset();
          }
        };
        display.detectingSelectAll = setTimeout(poll, 200);
      }
    }

    if (ie && ie_version >= 9) { prepareSelectAllHack(); }
    if (captureRightClick) {
      e_stop(e);
      var mouseup = function () {
        off(window, "mouseup", mouseup);
        setTimeout(rehide, 20);
      };
      on(window, "mouseup", mouseup);
    } else {
      setTimeout(rehide, 50);
    }
  };

  TextareaInput.prototype.readOnlyChanged = function (val) {
    if (!val) { this.reset(); }
    this.textarea.disabled = val == "nocursor";
  };

  TextareaInput.prototype.setUneditable = function () {};

  TextareaInput.prototype.needsContentAttribute = false;

  function fromTextArea(textarea, options) {
    options = options ? copyObj(options) : {};
    options.value = textarea.value;
    if (!options.tabindex && textarea.tabIndex)
      { options.tabindex = textarea.tabIndex; }
    if (!options.placeholder && textarea.placeholder)
      { options.placeholder = textarea.placeholder; }
    // Set autofocus to true if this textarea is focused, or if it has
    // autofocus and no other element is focused.
    if (options.autofocus == null) {
      var hasFocus = activeElt();
      options.autofocus = hasFocus == textarea ||
        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
    }

    function save() {textarea.value = cm.getValue();}

    var realSubmit;
    if (textarea.form) {
      on(textarea.form, "submit", save);
      // Deplorable hack to make the submit method do the right thing.
      if (!options.leaveSubmitMethodAlone) {
        var form = textarea.form;
        realSubmit = form.submit;
        try {
          var wrappedSubmit = form.submit = function () {
            save();
            form.submit = realSubmit;
            form.submit();
            form.submit = wrappedSubmit;
          };
        } catch(e) {}
      }
    }

    options.finishInit = function (cm) {
      cm.save = save;
      cm.getTextArea = function () { return textarea; };
      cm.toTextArea = function () {
        cm.toTextArea = isNaN; // Prevent this from being ran twice
        save();
        textarea.parentNode.removeChild(cm.getWrapperElement());
        textarea.style.display = "";
        if (textarea.form) {
          off(textarea.form, "submit", save);
          if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
            { textarea.form.submit = realSubmit; }
        }
      };
    };

    textarea.style.display = "none";
    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
      options);
    return cm
  }

  function addLegacyProps(CodeMirror) {
    CodeMirror.off = off;
    CodeMirror.on = on;
    CodeMirror.wheelEventPixels = wheelEventPixels;
    CodeMirror.Doc = Doc;
    CodeMirror.splitLines = splitLinesAuto;
    CodeMirror.countColumn = countColumn;
    CodeMirror.findColumn = findColumn;
    CodeMirror.isWordChar = isWordCharBasic;
    CodeMirror.Pass = Pass;
    CodeMirror.signal = signal;
    CodeMirror.Line = Line;
    CodeMirror.changeEnd = changeEnd;
    CodeMirror.scrollbarModel = scrollbarModel;
    CodeMirror.Pos = Pos;
    CodeMirror.cmpPos = cmp;
    CodeMirror.modes = modes;
    CodeMirror.mimeModes = mimeModes;
    CodeMirror.resolveMode = resolveMode;
    CodeMirror.getMode = getMode;
    CodeMirror.modeExtensions = modeExtensions;
    CodeMirror.extendMode = extendMode;
    CodeMirror.copyState = copyState;
    CodeMirror.startState = startState;
    CodeMirror.innerMode = innerMode;
    CodeMirror.commands = commands;
    CodeMirror.keyMap = keyMap;
    CodeMirror.keyName = keyName;
    CodeMirror.isModifierKey = isModifierKey;
    CodeMirror.lookupKey = lookupKey;
    CodeMirror.normalizeKeyMap = normalizeKeyMap;
    CodeMirror.StringStream = StringStream;
    CodeMirror.SharedTextMarker = SharedTextMarker;
    CodeMirror.TextMarker = TextMarker;
    CodeMirror.LineWidget = LineWidget;
    CodeMirror.e_preventDefault = e_preventDefault;
    CodeMirror.e_stopPropagation = e_stopPropagation;
    CodeMirror.e_stop = e_stop;
    CodeMirror.addClass = addClass;
    CodeMirror.contains = contains;
    CodeMirror.rmClass = rmClass;
    CodeMirror.keyNames = keyNames;
  }

  // EDITOR CONSTRUCTOR

  defineOptions(CodeMirror);

  addEditorMethods(CodeMirror);

  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
    { CodeMirror.prototype[prop] = (function(method) {
      return function() {return method.apply(this.doc, arguments)}
    })(Doc.prototype[prop]); } }

  eventMixin(Doc);
  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};

  // Extra arguments are stored as the mode's dependencies, which is
  // used by (legacy) mechanisms like loadmode.js to automatically
  // load a mode. (Preferred mechanism is the require/define calls.)
  CodeMirror.defineMode = function(name/*, mode, …*/) {
    if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
    defineMode.apply(this, arguments);
  };

  CodeMirror.defineMIME = defineMIME;

  // Minimal default mode.
  CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
  CodeMirror.defineMIME("text/plain", "null");

  // EXTENSIONS

  CodeMirror.defineExtension = function (name, func) {
    CodeMirror.prototype[name] = func;
  };
  CodeMirror.defineDocExtension = function (name, func) {
    Doc.prototype[name] = func;
  };

  CodeMirror.fromTextArea = fromTextArea;

  addLegacyProps(CodeMirror);

  CodeMirror.version = "5.52.0";

  return CodeMirror;

})));
;
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  CodeMirror.defineSimpleMode = function(name, states) {
    CodeMirror.defineMode(name, function(config) {
      return CodeMirror.simpleMode(config, states);
    });
  };

  CodeMirror.simpleMode = function(config, states) {
    ensureState(states, "start");
    var states_ = {}, meta = states.meta || {}, hasIndentation = false;
    for (var state in states) if (state != meta && states.hasOwnProperty(state)) {
      var list = states_[state] = [], orig = states[state];
      for (var i = 0; i < orig.length; i++) {
        var data = orig[i];
        list.push(new Rule(data, states));
        if (data.indent || data.dedent) hasIndentation = true;
      }
    }
    var mode = {
      startState: function() {
        return {state: "start", pending: null,
                local: null, localState: null,
                indent: hasIndentation ? [] : null};
      },
      copyState: function(state) {
        var s = {state: state.state, pending: state.pending,
                 local: state.local, localState: null,
                 indent: state.indent && state.indent.slice(0)};
        if (state.localState)
          s.localState = CodeMirror.copyState(state.local.mode, state.localState);
        if (state.stack)
          s.stack = state.stack.slice(0);
        for (var pers = state.persistentStates; pers; pers = pers.next)
          s.persistentStates = {mode: pers.mode,
                                spec: pers.spec,
                                state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),
                                next: s.persistentStates};
        return s;
      },
      token: tokenFunction(states_, config),
      innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; },
      indent: indentFunction(states_, meta)
    };
    if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop))
      mode[prop] = meta[prop];
    return mode;
  };

  function ensureState(states, name) {
    if (!states.hasOwnProperty(name))
      throw new Error("Undefined state " + name + " in simple mode");
  }

  function toRegex(val, caret) {
    if (!val) return /(?:)/;
    var flags = "";
    if (val instanceof RegExp) {
      if (val.ignoreCase) flags = "i";
      val = val.source;
    } else {
      val = String(val);
    }
    return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags);
  }

  function asToken(val) {
    if (!val) return null;
    if (val.apply) return val
    if (typeof val == "string") return val.replace(/\./g, " ");
    var result = [];
    for (var i = 0; i < val.length; i++)
      result.push(val[i] && val[i].replace(/\./g, " "));
    return result;
  }

  function Rule(data, states) {
    if (data.next || data.push) ensureState(states, data.next || data.push);
    this.regex = toRegex(data.regex);
    this.token = asToken(data.token);
    this.data = data;
  }

  function tokenFunction(states, config) {
    return function(stream, state) {
      if (state.pending) {
        var pend = state.pending.shift();
        if (state.pending.length == 0) state.pending = null;
        stream.pos += pend.text.length;
        return pend.token;
      }

      if (state.local) {
        if (state.local.end && stream.match(state.local.end)) {
          var tok = state.local.endToken || null;
          state.local = state.localState = null;
          return tok;
        } else {
          var tok = state.local.mode.token(stream, state.localState), m;
          if (state.local.endScan && (m = state.local.endScan.exec(stream.current())))
            stream.pos = stream.start + m.index;
          return tok;
        }
      }

      var curState = states[state.state];
      for (var i = 0; i < curState.length; i++) {
        var rule = curState[i];
        var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex);
        if (matches) {
          if (rule.data.next) {
            state.state = rule.data.next;
          } else if (rule.data.push) {
            (state.stack || (state.stack = [])).push(state.state);
            state.state = rule.data.push;
          } else if (rule.data.pop && state.stack && state.stack.length) {
            state.state = state.stack.pop();
          }

          if (rule.data.mode)
            enterLocalMode(config, state, rule.data.mode, rule.token);
          if (rule.data.indent)
            state.indent.push(stream.indentation() + config.indentUnit);
          if (rule.data.dedent)
            state.indent.pop();
          var token = rule.token
          if (token && token.apply) token = token(matches)
          if (matches.length > 2 && rule.token && typeof rule.token != "string") {
            state.pending = [];
            for (var j = 2; j < matches.length; j++)
              if (matches[j])
                state.pending.push({text: matches[j], token: rule.token[j - 1]});
            stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));
            return token[0];
          } else if (token && token.join) {
            return token[0];
          } else {
            return token;
          }
        }
      }
      stream.next();
      return null;
    };
  }

  function cmp(a, b) {
    if (a === b) return true;
    if (!a || typeof a != "object" || !b || typeof b != "object") return false;
    var props = 0;
    for (var prop in a) if (a.hasOwnProperty(prop)) {
      if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false;
      props++;
    }
    for (var prop in b) if (b.hasOwnProperty(prop)) props--;
    return props == 0;
  }

  function enterLocalMode(config, state, spec, token) {
    var pers;
    if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next)
      if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p;
    var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec);
    var lState = pers ? pers.state : CodeMirror.startState(mode);
    if (spec.persistent && !pers)
      state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates};

    state.localState = lState;
    state.local = {mode: mode,
                   end: spec.end && toRegex(spec.end),
                   endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),
                   endToken: token && token.join ? token[token.length - 1] : token};
  }

  function indexOf(val, arr) {
    for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true;
  }

  function indentFunction(states, meta) {
    return function(state, textAfter, line) {
      if (state.local && state.local.mode.indent)
        return state.local.mode.indent(state.localState, textAfter, line);
      if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)
        return CodeMirror.Pass;

      var pos = state.indent.length - 1, rules = states[state.state];
      scan: for (;;) {
        for (var i = 0; i < rules.length; i++) {
          var rule = rules[i];
          if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
            var m = rule.regex.exec(textAfter);
            if (m && m[0]) {
              pos--;
              if (rule.next || rule.push) rules = states[rule.next || rule.push];
              textAfter = textAfter.slice(m[0].length);
              continue scan;
            }
          }
        }
        break;
      }
      return pos < 0 ? 0 : state.indent[pos];
    };
  }
});
;
var emojiEssential={Activities:{"arts & crafts":{"🎨":"artist palette","🎭":"performing arts","🖼":"framed picture","🧵":"thread","🧶":"yarn"},"award-medal":{"♟":"chess pawn","♠":"spade suit","♣":"club suit","♥":"heart suit","♦":"diamond suit","⚽":"soccer ball","⚾":"baseball","⛳":"flag in hole","⛸":"ice skate","🀄":"mahjong red dragon","🃏":"joker","🎖":"military medal","🎣":"fishing pole","🎨":"artist palette","🎭":"performing arts","🎮":"video game","🎯":"direct hit","🎰":"slot machine","🎱":"pool 8 ball","🎲":"game die","🎳":"bowling","🎴":"flower playing cards","🎽":"running shirt","🎾":"tennis","🎿":"skis","🏀":"basketball","🏅":"sports medal","🏆":"trophy","🏈":"american football","🏉":"rugby football","🏏":"cricket game","🏐":"volleyball","🏑":"field hockey","🏒":"ice hockey","🏓":"ping pong","🏸":"badminton","🔮":"crystal ball","🕹":"joystick","🖼":"framed picture","🛷":"sled","🤿":"diving mask","🥅":"goal net","🥇":"1st place medal","🥈":"2nd place medal","🥉":"3rd place medal","🥊":"boxing glove","🥋":"martial arts uniform","🥌":"curling stone","🥍":"lacrosse","🥎":"softball","🥏":"flying disc","🧩":"puzzle piece","🧵":"thread","🧶":"yarn","🧸":"teddy bear","🧿":"nazar amulet","🪀":"yo-yo","🪁":"kite"},event:{"♟":"chess pawn","♠":"spade suit","♣":"club suit","♥":"heart suit","♦":"diamond suit","⚽":"soccer ball","⚾":"baseball","⛳":"flag in hole","⛸":"ice skate","✨":"sparkles","🀄":"mahjong red dragon","🃏":"joker","🎀":"ribbon","🎁":"wrapped gift","🎃":"jack-o-lantern","🎄":"Christmas tree","🎆":"fireworks","🎇":"sparkler","🎈":"balloon","🎉":"party popper","🎊":"confetti ball","🎋":"tanabata tree","🎍":"pine decoration","🎎":"Japanese dolls","🎏":"carp streamer","🎐":"wind chime","🎑":"moon viewing ceremony","🎖":"military medal","🎗":"reminder ribbon","🎟":"admission tickets","🎣":"fishing pole","🎨":"artist palette","🎫":"ticket","🎭":"performing arts","🎮":"video game","🎯":"direct hit","🎰":"slot machine","🎱":"pool 8 ball","🎲":"game die","🎳":"bowling","🎴":"flower playing cards","🎽":"running shirt","🎾":"tennis","🎿":"skis","🏀":"basketball","🏅":"sports medal","🏆":"trophy","🏈":"american football","🏉":"rugby football","🏏":"cricket game","🏐":"volleyball","🏑":"field hockey","🏒":"ice hockey","🏓":"ping pong","🏸":"badminton","🔮":"crystal ball","🕹":"joystick","🖼":"framed picture","🛷":"sled","🤿":"diving mask","🥅":"goal net","🥇":"1st place medal","🥈":"2nd place medal","🥉":"3rd place medal","🥊":"boxing glove","🥋":"martial arts uniform","🥌":"curling stone","🥍":"lacrosse","🥎":"softball","🥏":"flying disc","🧧":"red envelope","🧨":"firecracker","🧩":"puzzle piece","🧵":"thread","🧶":"yarn","🧸":"teddy bear","🧿":"nazar amulet","🪀":"yo-yo","🪁":"kite"},game:{"♟":"chess pawn","♠":"spade suit","♣":"club suit","♥":"heart suit","♦":"diamond suit","🀄":"mahjong red dragon","🃏":"joker","🎨":"artist palette","🎭":"performing arts","🎮":"video game","🎯":"direct hit","🎰":"slot machine","🎱":"pool 8 ball","🎲":"game die","🎴":"flower playing cards","🔮":"crystal ball","🕹":"joystick","🖼":"framed picture","🧩":"puzzle piece","🧵":"thread","🧶":"yarn","🧸":"teddy bear","🧿":"nazar amulet","🪀":"yo-yo","🪁":"kite"},sport:{"♟":"chess pawn","♠":"spade suit","♣":"club suit","♥":"heart suit","♦":"diamond suit","⚽":"soccer ball","⚾":"baseball","⛳":"flag in hole","⛸":"ice skate","🀄":"mahjong red dragon","🃏":"joker","🎣":"fishing pole","🎨":"artist palette","🎭":"performing arts","🎮":"video game","🎯":"direct hit","🎰":"slot machine","🎱":"pool 8 ball","🎲":"game die","🎳":"bowling","🎴":"flower playing cards","🎽":"running shirt","🎾":"tennis","🎿":"skis","🏀":"basketball","🏈":"american football","🏉":"rugby football","🏏":"cricket game","🏐":"volleyball","🏑":"field hockey","🏒":"ice hockey","🏓":"ping pong","🏸":"badminton","🔮":"crystal ball","🕹":"joystick","🖼":"framed picture","🛷":"sled","🤿":"diving mask","🥅":"goal net","🥊":"boxing glove","🥋":"martial arts uniform","🥌":"curling stone","🥍":"lacrosse","🥎":"softball","🥏":"flying disc","🧩":"puzzle piece","🧵":"thread","🧶":"yarn","🧸":"teddy bear","🧿":"nazar amulet","🪀":"yo-yo","🪁":"kite"}},"Animals & Nature":{"animal-amphibian":{"☘":"shamrock","🌱":"seedling","🌲":"evergreen tree","🌳":"deciduous tree","🌴":"palm tree","🌵":"cactus","🌷":"tulip","🌸":"cherry blossom","🌹":"rose","🌺":"hibiscus","🌻":"sunflower","🌼":"blossom","🌾":"sheaf of rice","🌿":"herb","🍀":"four leaf clover","🍁":"maple leaf","🍂":"fallen leaf","🍃":"leaf fluttering in wind","🏵":"rosette","🐉":"dragon","🐊":"crocodile","🐋":"whale","🐌":"snail","🐍":"snake","🐙":"octopus","🐚":"spiral shell","🐛":"bug","🐜":"ant","🐝":"honeybee","🐞":"lady beetle","🐟":"fish","🐠":"tropical fish","🐡":"blowfish","🐢":"turtle","🐬":"dolphin","🐲":"dragon face","🐳":"spouting whale","🐸":"frog","💐":"bouquet","💮":"white flower","🕷":"spider","🕸":"spider web","🥀":"wilted flower","🦂":"scorpion","🦈":"shark","🦋":"butterfly","🦎":"lizard","🦕":"sauropod","🦖":"T-Rex","🦗":"cricket","🦟":"mosquito","🦠":"microbe"},"animal-bird":{"☘":"shamrock","🌱":"seedling","🌲":"evergreen tree","🌳":"deciduous tree","🌴":"palm tree","🌵":"cactus","🌷":"tulip","🌸":"cherry blossom","🌹":"rose","🌺":"hibiscus","🌻":"sunflower","🌼":"blossom","🌾":"sheaf of rice","🌿":"herb","🍀":"four leaf clover","🍁":"maple leaf","🍂":"fallen leaf","🍃":"leaf fluttering in wind","🏵":"rosette","🐉":"dragon","🐊":"crocodile","🐋":"whale","🐌":"snail","🐍":"snake","🐓":"rooster","🐔":"chicken","🐙":"octopus","🐚":"spiral shell","🐛":"bug","🐜":"ant","🐝":"honeybee","🐞":"lady beetle","🐟":"fish","🐠":"tropical fish","🐡":"blowfish","🐢":"turtle","🐣":"hatching chick","🐤":"baby chick","🐥":"front-facing baby chick","🐦":"bird","🐧":"penguin","🐬":"dolphin","🐲":"dragon face","🐳":"spouting whale","🐸":"frog","💐":"bouquet","💮":"white flower","🕊":"dove","🕷":"spider","🕸":"spider web","🥀":"wilted flower","🦂":"scorpion","🦃":"turkey","🦅":"eagle","🦆":"duck","🦈":"shark","🦉":"owl","🦋":"butterfly","🦎":"lizard","🦕":"sauropod","🦖":"T-Rex","🦗":"cricket","🦚":"peacock","🦜":"parrot","🦟":"mosquito","🦠":"microbe","🦢":"swan","🦩":"flamingo"},"animal-bug":{"☘":"shamrock","🌱":"seedling","🌲":"evergreen tree","🌳":"deciduous tree","🌴":"palm tree","🌵":"cactus","🌷":"tulip","🌸":"cherry blossom","🌹":"rose","🌺":"hibiscus","🌻":"sunflower","🌼":"blossom","🌾":"sheaf of rice","🌿":"herb","🍀":"four leaf clover","🍁":"maple leaf","🍂":"fallen leaf","🍃":"leaf fluttering in wind","🏵":"rosette","🐌":"snail","🐛":"bug","🐜":"ant","🐝":"honeybee","🐞":"lady beetle","💐":"bouquet","💮":"white flower","🕷":"spider","🕸":"spider web","🥀":"wilted flower","🦂":"scorpion","🦋":"butterfly","🦗":"cricket","🦟":"mosquito","🦠":"microbe"},"animal-mammal":{"☘":"shamrock","🌱":"seedling","🌲":"evergreen tree","🌳":"deciduous tree","🌴":"palm tree","🌵":"cactus","🌷":"tulip","🌸":"cherry blossom","🌹":"rose","🌺":"hibiscus","🌻":"sunflower","🌼":"blossom","🌾":"sheaf of rice","🌿":"herb","🍀":"four leaf clover","🍁":"maple leaf","🍂":"fallen leaf","🍃":"leaf fluttering in wind","🏵":"rosette","🐀":"rat","🐁":"mouse","🐂":"ox","🐃":"water buffalo","🐄":"cow","🐅":"tiger","🐆":"leopard","🐇":"rabbit","🐈":"cat","🐉":"dragon","🐊":"crocodile","🐋":"whale","🐌":"snail","🐍":"snake","🐎":"horse","🐏":"ram","🐐":"goat","🐑":"ewe","🐒":"monkey","🐓":"rooster","🐔":"chicken","🐕":"dog","🐕‍🦺":"service dog","🐖":"pig","🐗":"boar","🐘":"elephant","🐙":"octopus","🐚":"spiral shell","🐛":"bug","🐜":"ant","🐝":"honeybee","🐞":"lady beetle","🐟":"fish","🐠":"tropical fish","🐡":"blowfish","🐢":"turtle","🐣":"hatching chick","🐤":"baby chick","🐥":"front-facing baby chick","🐦":"bird","🐧":"penguin","🐨":"koala","🐩":"poodle","🐪":"camel","🐫":"two-hump camel","🐬":"dolphin","🐭":"mouse face","🐮":"cow face","🐯":"tiger face","🐰":"rabbit face","🐱":"cat face","🐲":"dragon face","🐳":"spouting whale","🐴":"horse face","🐵":"monkey face","🐶":"dog face","🐷":"pig face","🐸":"frog","🐹":"hamster","🐺":"wolf","🐻":"bear","🐼":"panda","🐽":"pig nose","🐾":"paw prints","🐿":"chipmunk","💐":"bouquet","💮":"white flower","🕊":"dove","🕷":"spider","🕸":"spider web","🥀":"wilted flower","🦁":"lion","🦂":"scorpion","🦃":"turkey","🦄":"unicorn","🦅":"eagle","🦆":"duck","🦇":"bat","🦈":"shark","🦉":"owl","🦊":"fox","🦋":"butterfly","🦌":"deer","🦍":"gorilla","🦎":"lizard","🦏":"rhinoceros","🦒":"giraffe","🦓":"zebra","🦔":"hedgehog","🦕":"sauropod","🦖":"T-Rex","🦗":"cricket","🦘":"kangaroo","🦙":"llama","🦚":"peacock","🦛":"hippopotamus","🦜":"parrot","🦝":"raccoon","🦟":"mosquito","🦠":"microbe","🦡":"badger","🦢":"swan","🦥":"sloth","🦦":"otter","🦧":"orangutan","🦨":"skunk","🦩":"flamingo","🦮":"guide dog"},"animal-marine":{"☘":"shamrock","🌱":"seedling","🌲":"evergreen tree","🌳":"deciduous tree","🌴":"palm tree","🌵":"cactus","🌷":"tulip","🌸":"cherry blossom","🌹":"rose","🌺":"hibiscus","🌻":"sunflower","🌼":"blossom","🌾":"sheaf of rice","🌿":"herb","🍀":"four leaf clover","🍁":"maple leaf","🍂":"fallen leaf","🍃":"leaf fluttering in wind","🏵":"rosette","🐋":"whale","🐌":"snail","🐙":"octopus","🐚":"spiral shell","🐛":"bug","🐜":"ant","🐝":"honeybee","🐞":"lady beetle","🐟":"fish","🐠":"tropical fish","🐡":"blowfish","🐬":"dolphin","🐳":"spouting whale","💐":"bouquet","💮":"white flower","🕷":"spider","🕸":"spider web","🥀":"wilted flower","🦂":"scorpion","🦈":"shark","🦋":"butterfly","🦗":"cricket","🦟":"mosquito","🦠":"microbe"},"animal-reptile":{"☘":"shamrock","🌱":"seedling","🌲":"evergreen tree","🌳":"deciduous tree","🌴":"palm tree","🌵":"cactus","🌷":"tulip","🌸":"cherry blossom","🌹":"rose","🌺":"hibiscus","🌻":"sunflower","🌼":"blossom","🌾":"sheaf of rice","🌿":"herb","🍀":"four leaf clover","🍁":"maple leaf","🍂":"fallen leaf","🍃":"leaf fluttering in wind","🏵":"rosette","🐉":"dragon","🐊":"crocodile","🐋":"whale","🐌":"snail","🐍":"snake","🐙":"octopus","🐚":"spiral shell","🐛":"bug","🐜":"ant","🐝":"honeybee","🐞":"lady beetle","🐟":"fish","🐠":"tropical fish","🐡":"blowfish","🐢":"turtle","🐬":"dolphin","🐲":"dragon face","🐳":"spouting whale","💐":"bouquet","💮":"white flower","🕷":"spider","🕸":"spider web","🥀":"wilted flower","🦂":"scorpion","🦈":"shark","🦋":"butterfly","🦎":"lizard","🦕":"sauropod","🦖":"T-Rex","🦗":"cricket","🦟":"mosquito","🦠":"microbe"},"plant-flower":{"☘":"shamrock","🌱":"seedling","🌲":"evergreen tree","🌳":"deciduous tree","🌴":"palm tree","🌵":"cactus","🌷":"tulip","🌸":"cherry blossom","🌹":"rose","🌺":"hibiscus","🌻":"sunflower","🌼":"blossom","🌾":"sheaf of rice","🌿":"herb","🍀":"four leaf clover","🍁":"maple leaf","🍂":"fallen leaf","🍃":"leaf fluttering in wind","🏵":"rosette","💐":"bouquet","💮":"white flower","🥀":"wilted flower"},"plant-other":{"☘":"shamrock","🌱":"seedling","🌲":"evergreen tree","🌳":"deciduous tree","🌴":"palm tree","🌵":"cactus","🌾":"sheaf of rice","🌿":"herb","🍀":"four leaf clover","🍁":"maple leaf","🍂":"fallen leaf","🍃":"leaf fluttering in wind"}},Component:{"hair-style":{"🦰":"red hair","🦱":"curly hair","🦲":"bald","🦳":"white hair"}},Flags:{"country-flag":{"🇦🇨":"flag: Ascension Island","🇦🇩":"flag: Andorra","🇦🇪":"flag: United Arab Emirates","🇦🇫":"flag: Afghanistan","🇦🇬":"flag: Antigua & Barbuda","🇦🇮":"flag: Anguilla","🇦🇱":"flag: Albania","🇦🇲":"flag: Armenia","🇦🇴":"flag: Angola","🇦🇶":"flag: Antarctica","🇦🇷":"flag: Argentina","🇦🇸":"flag: American Samoa","🇦🇹":"flag: Austria","🇦🇺":"flag: Australia","🇦🇼":"flag: Aruba","🇦🇽":"flag: Åland Islands","🇦🇿":"flag: Azerbaijan","🇧🇦":"flag: Bosnia & Herzegovina","🇧🇧":"flag: Barbados","🇧🇩":"flag: Bangladesh","🇧🇪":"flag: Belgium","🇧🇫":"flag: Burkina Faso","🇧🇬":"flag: Bulgaria","🇧🇭":"flag: Bahrain","🇧🇮":"flag: Burundi","🇧🇯":"flag: Benin","🇧🇱":"flag: St. Barthélemy","🇧🇲":"flag: Bermuda","🇧🇳":"flag: Brunei","🇧🇴":"flag: Bolivia","🇧🇶":"flag: Caribbean Netherlands","🇧🇷":"flag: Brazil","🇧🇸":"flag: Bahamas","🇧🇹":"flag: Bhutan","🇧🇻":"flag: Bouvet Island","🇧🇼":"flag: Botswana","🇧🇾":"flag: Belarus","🇧🇿":"flag: Belize","🇨🇦":"flag: Canada","🇨🇨":"flag: Cocos (Keeling) Islands","🇨🇩":"flag: Congo - Kinshasa","🇨🇫":"flag: Central African Republic","🇨🇬":"flag: Congo - Brazzaville","🇨🇭":"flag: Switzerland","🇨🇮":"flag: Côte d’Ivoire","🇨🇰":"flag: Cook Islands","🇨🇱":"flag: Chile","🇨🇲":"flag: Cameroon","🇨🇳":"flag: China","🇨🇴":"flag: Colombia","🇨🇵":"flag: Clipperton Island","🇨🇷":"flag: Costa Rica","🇨🇺":"flag: Cuba","🇨🇻":"flag: Cape Verde","🇨🇼":"flag: Curaçao","🇨🇽":"flag: Christmas Island","🇨🇾":"flag: Cyprus","🇨🇿":"flag: Czechia","🇩🇪":"flag: Germany","🇩🇬":"flag: Diego Garcia","🇩🇯":"flag: Djibouti","🇩🇰":"flag: Denmark","🇩🇲":"flag: Dominica","🇩🇴":"flag: Dominican Republic","🇩🇿":"flag: Algeria","🇪🇦":"flag: Ceuta & Melilla","🇪🇨":"flag: Ecuador","🇪🇪":"flag: Estonia","🇪🇬":"flag: Egypt","🇪🇭":"flag: Western Sahara","🇪🇷":"flag: Eritrea","🇪🇸":"flag: Spain","🇪🇹":"flag: Ethiopia","🇪🇺":"flag: European Union","🇫🇮":"flag: Finland","🇫🇯":"flag: Fiji","🇫🇰":"flag: Falkland Islands","🇫🇲":"flag: Micronesia","🇫🇴":"flag: Faroe Islands","🇫🇷":"flag: France","🇬🇦":"flag: Gabon","🇬🇧":"flag: United Kingdom","🇬🇩":"flag: Grenada","🇬🇪":"flag: Georgia","🇬🇫":"flag: French Guiana","🇬🇬":"flag: Guernsey","🇬🇭":"flag: Ghana","🇬🇮":"flag: Gibraltar","🇬🇱":"flag: Greenland","🇬🇲":"flag: Gambia","🇬🇳":"flag: Guinea","🇬🇵":"flag: Guadeloupe","🇬🇶":"flag: Equatorial Guinea","🇬🇷":"flag: Greece","🇬🇸":"flag: South Georgia & South Sandwich Islands","🇬🇹":"flag: Guatemala","🇬🇺":"flag: Guam","🇬🇼":"flag: Guinea-Bissau","🇬🇾":"flag: Guyana","🇭🇰":"flag: Hong Kong SAR China","🇭🇲":"flag: Heard & McDonald Islands","🇭🇳":"flag: Honduras","🇭🇷":"flag: Croatia","🇭🇹":"flag: Haiti","🇭🇺":"flag: Hungary","🇮🇨":"flag: Canary Islands","🇮🇩":"flag: Indonesia","🇮🇪":"flag: Ireland","🇮🇱":"flag: Israel","🇮🇲":"flag: Isle of Man","🇮🇳":"flag: India","🇮🇴":"flag: British Indian Ocean Territory","🇮🇶":"flag: Iraq","🇮🇷":"flag: Iran","🇮🇸":"flag: Iceland","🇮🇹":"flag: Italy","🇯🇪":"flag: Jersey","🇯🇲":"flag: Jamaica","🇯🇴":"flag: Jordan","🇯🇵":"flag: Japan","🇰🇪":"flag: Kenya","🇰🇬":"flag: Kyrgyzstan","🇰🇭":"flag: Cambodia","🇰🇮":"flag: Kiribati","🇰🇲":"flag: Comoros","🇰🇳":"flag: St. Kitts & Nevis","🇰🇵":"flag: North Korea","🇰🇷":"flag: South Korea","🇰🇼":"flag: Kuwait","🇰🇾":"flag: Cayman Islands","🇰🇿":"flag: Kazakhstan","🇱🇦":"flag: Laos","🇱🇧":"flag: Lebanon","🇱🇨":"flag: St. Lucia","🇱🇮":"flag: Liechtenstein","🇱🇰":"flag: Sri Lanka","🇱🇷":"flag: Liberia","🇱🇸":"flag: Lesotho","🇱🇹":"flag: Lithuania","🇱🇺":"flag: Luxembourg","🇱🇻":"flag: Latvia","🇱🇾":"flag: Libya","🇲🇦":"flag: Morocco","🇲🇨":"flag: Monaco","🇲🇩":"flag: Moldova","🇲🇪":"flag: Montenegro","🇲🇫":"flag: St. Martin","🇲🇬":"flag: Madagascar","🇲🇭":"flag: Marshall Islands","🇲🇰":"flag: North Macedonia","🇲🇱":"flag: Mali","🇲🇲":"flag: Myanmar (Burma)","🇲🇳":"flag: Mongolia","🇲🇴":"flag: Macao SAR China","🇲🇵":"flag: Northern Mariana Islands","🇲🇶":"flag: Martinique","🇲🇷":"flag: Mauritania","🇲🇸":"flag: Montserrat","🇲🇹":"flag: Malta","🇲🇺":"flag: Mauritius","🇲🇻":"flag: Maldives","🇲🇼":"flag: Malawi","🇲🇽":"flag: Mexico","🇲🇾":"flag: Malaysia","🇲🇿":"flag: Mozambique","🇳🇦":"flag: Namibia","🇳🇨":"flag: New Caledonia","🇳🇪":"flag: Niger","🇳🇫":"flag: Norfolk Island","🇳🇬":"flag: Nigeria","🇳🇮":"flag: Nicaragua","🇳🇱":"flag: Netherlands","🇳🇴":"flag: Norway","🇳🇵":"flag: Nepal","🇳🇷":"flag: Nauru","🇳🇺":"flag: Niue","🇳🇿":"flag: New Zealand","🇴🇲":"flag: Oman","🇵🇦":"flag: Panama","🇵🇪":"flag: Peru","🇵🇫":"flag: French Polynesia","🇵🇬":"flag: Papua New Guinea","🇵🇭":"flag: Philippines","🇵🇰":"flag: Pakistan","🇵🇱":"flag: Poland","🇵🇲":"flag: St. Pierre & Miquelon","🇵🇳":"flag: Pitcairn Islands","🇵🇷":"flag: Puerto Rico","🇵🇸":"flag: Palestinian Territories","🇵🇹":"flag: Portugal","🇵🇼":"flag: Palau","🇵🇾":"flag: Paraguay","🇶🇦":"flag: Qatar","🇷🇪":"flag: Réunion","🇷🇴":"flag: Romania","🇷🇸":"flag: Serbia","🇷🇺":"flag: Russia","🇷🇼":"flag: Rwanda","🇸🇦":"flag: Saudi Arabia","🇸🇧":"flag: Solomon Islands","🇸🇨":"flag: Seychelles","🇸🇩":"flag: Sudan","🇸🇪":"flag: Sweden","🇸🇬":"flag: Singapore","🇸🇭":"flag: St. Helena","🇸🇮":"flag: Slovenia","🇸🇯":"flag: Svalbard & Jan Mayen","🇸🇰":"flag: Slovakia","🇸🇱":"flag: Sierra Leone","🇸🇲":"flag: San Marino","🇸🇳":"flag: Senegal","🇸🇴":"flag: Somalia","🇸🇷":"flag: Suriname","🇸🇸":"flag: South Sudan","🇸🇹":"flag: São Tomé & Príncipe","🇸🇻":"flag: El Salvador","🇸🇽":"flag: Sint Maarten","🇸🇾":"flag: Syria","🇸🇿":"flag: Eswatini","🇹🇦":"flag: Tristan da Cunha","🇹🇨":"flag: Turks & Caicos Islands","🇹🇩":"flag: Chad","🇹🇫":"flag: French Southern Territories","🇹🇬":"flag: Togo","🇹🇭":"flag: Thailand","🇹🇯":"flag: Tajikistan","🇹🇰":"flag: Tokelau","🇹🇱":"flag: Timor-Leste","🇹🇲":"flag: Turkmenistan","🇹🇳":"flag: Tunisia","🇹🇴":"flag: Tonga","🇹🇷":"flag: Turkey","🇹🇹":"flag: Trinidad & Tobago","🇹🇻":"flag: Tuvalu","🇹🇼":"flag: Taiwan","🇹🇿":"flag: Tanzania","🇺🇦":"flag: Ukraine","🇺🇬":"flag: Uganda","🇺🇲":"flag: U.S. Outlying Islands","🇺🇳":"flag: United Nations","🇺🇸":"flag: United States","🇺🇾":"flag: Uruguay","🇺🇿":"flag: Uzbekistan","🇻🇦":"flag: Vatican City","🇻🇨":"flag: St. Vincent & Grenadines","🇻🇪":"flag: Venezuela","🇻🇬":"flag: British Virgin Islands","🇻🇮":"flag: U.S. Virgin Islands","🇻🇳":"flag: Vietnam","🇻🇺":"flag: Vanuatu","🇼🇫":"flag: Wallis & Futuna","🇼🇸":"flag: Samoa","🇽🇰":"flag: Kosovo","🇾🇪":"flag: Yemen","🇾🇹":"flag: Mayotte","🇿🇦":"flag: South Africa","🇿🇲":"flag: Zambia","🇿🇼":"flag: Zimbabwe","🏴󠁧󠁢󠁥󠁮󠁧󠁿":"flag: England","🏴󠁧󠁢󠁳󠁣󠁴󠁿":"flag: Scotland","🏴󠁧󠁢󠁷󠁬󠁳󠁿":"flag: Wales"},flag:{"🇦🇨":"flag: Ascension Island","🇦🇩":"flag: Andorra","🇦🇪":"flag: United Arab Emirates","🇦🇫":"flag: Afghanistan","🇦🇬":"flag: Antigua & Barbuda","🇦🇮":"flag: Anguilla","🇦🇱":"flag: Albania","🇦🇲":"flag: Armenia","🇦🇴":"flag: Angola","🇦🇶":"flag: Antarctica","🇦🇷":"flag: Argentina","🇦🇸":"flag: American Samoa","🇦🇹":"flag: Austria","🇦🇺":"flag: Australia","🇦🇼":"flag: Aruba","🇦🇽":"flag: Åland Islands","🇦🇿":"flag: Azerbaijan","🇧🇦":"flag: Bosnia & Herzegovina","🇧🇧":"flag: Barbados","🇧🇩":"flag: Bangladesh","🇧🇪":"flag: Belgium","🇧🇫":"flag: Burkina Faso","🇧🇬":"flag: Bulgaria","🇧🇭":"flag: Bahrain","🇧🇮":"flag: Burundi","🇧🇯":"flag: Benin","🇧🇱":"flag: St. Barthélemy","🇧🇲":"flag: Bermuda","🇧🇳":"flag: Brunei","🇧🇴":"flag: Bolivia","🇧🇶":"flag: Caribbean Netherlands","🇧🇷":"flag: Brazil","🇧🇸":"flag: Bahamas","🇧🇹":"flag: Bhutan","🇧🇻":"flag: Bouvet Island","🇧🇼":"flag: Botswana","🇧🇾":"flag: Belarus","🇧🇿":"flag: Belize","🇨🇦":"flag: Canada","🇨🇨":"flag: Cocos (Keeling) Islands","🇨🇩":"flag: Congo - Kinshasa","🇨🇫":"flag: Central African Republic","🇨🇬":"flag: Congo - Brazzaville","🇨🇭":"flag: Switzerland","🇨🇮":"flag: Côte d’Ivoire","🇨🇰":"flag: Cook Islands","🇨🇱":"flag: Chile","🇨🇲":"flag: Cameroon","🇨🇳":"flag: China","🇨🇴":"flag: Colombia","🇨🇵":"flag: Clipperton Island","🇨🇷":"flag: Costa Rica","🇨🇺":"flag: Cuba","🇨🇻":"flag: Cape Verde","🇨🇼":"flag: Curaçao","🇨🇽":"flag: Christmas Island","🇨🇾":"flag: Cyprus","🇨🇿":"flag: Czechia","🇩🇪":"flag: Germany","🇩🇬":"flag: Diego Garcia","🇩🇯":"flag: Djibouti","🇩🇰":"flag: Denmark","🇩🇲":"flag: Dominica","🇩🇴":"flag: Dominican Republic","🇩🇿":"flag: Algeria","🇪🇦":"flag: Ceuta & Melilla","🇪🇨":"flag: Ecuador","🇪🇪":"flag: Estonia","🇪🇬":"flag: Egypt","🇪🇭":"flag: Western Sahara","🇪🇷":"flag: Eritrea","🇪🇸":"flag: Spain","🇪🇹":"flag: Ethiopia","🇪🇺":"flag: European Union","🇫🇮":"flag: Finland","🇫🇯":"flag: Fiji","🇫🇰":"flag: Falkland Islands","🇫🇲":"flag: Micronesia","🇫🇴":"flag: Faroe Islands","🇫🇷":"flag: France","🇬🇦":"flag: Gabon","🇬🇧":"flag: United Kingdom","🇬🇩":"flag: Grenada","🇬🇪":"flag: Georgia","🇬🇫":"flag: French Guiana","🇬🇬":"flag: Guernsey","🇬🇭":"flag: Ghana","🇬🇮":"flag: Gibraltar","🇬🇱":"flag: Greenland","🇬🇲":"flag: Gambia","🇬🇳":"flag: Guinea","🇬🇵":"flag: Guadeloupe","🇬🇶":"flag: Equatorial Guinea","🇬🇷":"flag: Greece","🇬🇸":"flag: South Georgia & South Sandwich Islands","🇬🇹":"flag: Guatemala","🇬🇺":"flag: Guam","🇬🇼":"flag: Guinea-Bissau","🇬🇾":"flag: Guyana","🇭🇰":"flag: Hong Kong SAR China","🇭🇲":"flag: Heard & McDonald Islands","🇭🇳":"flag: Honduras","🇭🇷":"flag: Croatia","🇭🇹":"flag: Haiti","🇭🇺":"flag: Hungary","🇮🇨":"flag: Canary Islands","🇮🇩":"flag: Indonesia","🇮🇪":"flag: Ireland","🇮🇱":"flag: Israel","🇮🇲":"flag: Isle of Man","🇮🇳":"flag: India","🇮🇴":"flag: British Indian Ocean Territory","🇮🇶":"flag: Iraq","🇮🇷":"flag: Iran","🇮🇸":"flag: Iceland","🇮🇹":"flag: Italy","🇯🇪":"flag: Jersey","🇯🇲":"flag: Jamaica","🇯🇴":"flag: Jordan","🇯🇵":"flag: Japan","🇰🇪":"flag: Kenya","🇰🇬":"flag: Kyrgyzstan","🇰🇭":"flag: Cambodia","🇰🇮":"flag: Kiribati","🇰🇲":"flag: Comoros","🇰🇳":"flag: St. Kitts & Nevis","🇰🇵":"flag: North Korea","🇰🇷":"flag: South Korea","🇰🇼":"flag: Kuwait","🇰🇾":"flag: Cayman Islands","🇰🇿":"flag: Kazakhstan","🇱🇦":"flag: Laos","🇱🇧":"flag: Lebanon","🇱🇨":"flag: St. Lucia","🇱🇮":"flag: Liechtenstein","🇱🇰":"flag: Sri Lanka","🇱🇷":"flag: Liberia","🇱🇸":"flag: Lesotho","🇱🇹":"flag: Lithuania","🇱🇺":"flag: Luxembourg","🇱🇻":"flag: Latvia","🇱🇾":"flag: Libya","🇲🇦":"flag: Morocco","🇲🇨":"flag: Monaco","🇲🇩":"flag: Moldova","🇲🇪":"flag: Montenegro","🇲🇫":"flag: St. Martin","🇲🇬":"flag: Madagascar","🇲🇭":"flag: Marshall Islands","🇲🇰":"flag: North Macedonia","🇲🇱":"flag: Mali","🇲🇲":"flag: Myanmar (Burma)","🇲🇳":"flag: Mongolia","🇲🇴":"flag: Macao SAR China","🇲🇵":"flag: Northern Mariana Islands","🇲🇶":"flag: Martinique","🇲🇷":"flag: Mauritania","🇲🇸":"flag: Montserrat","🇲🇹":"flag: Malta","🇲🇺":"flag: Mauritius","🇲🇻":"flag: Maldives","🇲🇼":"flag: Malawi","🇲🇽":"flag: Mexico","🇲🇾":"flag: Malaysia","🇲🇿":"flag: Mozambique","🇳🇦":"flag: Namibia","🇳🇨":"flag: New Caledonia","🇳🇪":"flag: Niger","🇳🇫":"flag: Norfolk Island","🇳🇬":"flag: Nigeria","🇳🇮":"flag: Nicaragua","🇳🇱":"flag: Netherlands","🇳🇴":"flag: Norway","🇳🇵":"flag: Nepal","🇳🇷":"flag: Nauru","🇳🇺":"flag: Niue","🇳🇿":"flag: New Zealand","🇴🇲":"flag: Oman","🇵🇦":"flag: Panama","🇵🇪":"flag: Peru","🇵🇫":"flag: French Polynesia","🇵🇬":"flag: Papua New Guinea","🇵🇭":"flag: Philippines","🇵🇰":"flag: Pakistan","🇵🇱":"flag: Poland","🇵🇲":"flag: St. Pierre & Miquelon","🇵🇳":"flag: Pitcairn Islands","🇵🇷":"flag: Puerto Rico","🇵🇸":"flag: Palestinian Territories","🇵🇹":"flag: Portugal","🇵🇼":"flag: Palau","🇵🇾":"flag: Paraguay","🇶🇦":"flag: Qatar","🇷🇪":"flag: Réunion","🇷🇴":"flag: Romania","🇷🇸":"flag: Serbia","🇷🇺":"flag: Russia","🇷🇼":"flag: Rwanda","🇸🇦":"flag: Saudi Arabia","🇸🇧":"flag: Solomon Islands","🇸🇨":"flag: Seychelles","🇸🇩":"flag: Sudan","🇸🇪":"flag: Sweden","🇸🇬":"flag: Singapore","🇸🇭":"flag: St. Helena","🇸🇮":"flag: Slovenia","🇸🇯":"flag: Svalbard & Jan Mayen","🇸🇰":"flag: Slovakia","🇸🇱":"flag: Sierra Leone","🇸🇲":"flag: San Marino","🇸🇳":"flag: Senegal","🇸🇴":"flag: Somalia","🇸🇷":"flag: Suriname","🇸🇸":"flag: South Sudan","🇸🇹":"flag: São Tomé & Príncipe","🇸🇻":"flag: El Salvador","🇸🇽":"flag: Sint Maarten","🇸🇾":"flag: Syria","🇸🇿":"flag: Eswatini","🇹🇦":"flag: Tristan da Cunha","🇹🇨":"flag: Turks & Caicos Islands","🇹🇩":"flag: Chad","🇹🇫":"flag: French Southern Territories","🇹🇬":"flag: Togo","🇹🇭":"flag: Thailand","🇹🇯":"flag: Tajikistan","🇹🇰":"flag: Tokelau","🇹🇱":"flag: Timor-Leste","🇹🇲":"flag: Turkmenistan","🇹🇳":"flag: Tunisia","🇹🇴":"flag: Tonga","🇹🇷":"flag: Turkey","🇹🇹":"flag: Trinidad & Tobago","🇹🇻":"flag: Tuvalu","🇹🇼":"flag: Taiwan","🇹🇿":"flag: Tanzania","🇺🇦":"flag: Ukraine","🇺🇬":"flag: Uganda","🇺🇲":"flag: U.S. Outlying Islands","🇺🇳":"flag: United Nations","🇺🇸":"flag: United States","🇺🇾":"flag: Uruguay","🇺🇿":"flag: Uzbekistan","🇻🇦":"flag: Vatican City","🇻🇨":"flag: St. Vincent & Grenadines","🇻🇪":"flag: Venezuela","🇻🇬":"flag: British Virgin Islands","🇻🇮":"flag: U.S. Virgin Islands","🇻🇳":"flag: Vietnam","🇻🇺":"flag: Vanuatu","🇼🇫":"flag: Wallis & Futuna","🇼🇸":"flag: Samoa","🇽🇰":"flag: Kosovo","🇾🇪":"flag: Yemen","🇾🇹":"flag: Mayotte","🇿🇦":"flag: South Africa","🇿🇲":"flag: Zambia","🇿🇼":"flag: Zimbabwe","🎌":"crossed flags","🏁":"chequered flag","🏳":"white flag","🏳️‍🌈":"rainbow flag","🏴":"black flag","🏴‍☠️":"pirate flag","🏴󠁧󠁢󠁥󠁮󠁧󠁿":"flag: England","🏴󠁧󠁢󠁳󠁣󠁴󠁿":"flag: Scotland","🏴󠁧󠁢󠁷󠁬󠁳󠁿":"flag: Wales","🚩":"triangular flag"},"subdivision-flag":{"🏴󠁧󠁢󠁥󠁮󠁧󠁿":"flag: England","🏴󠁧󠁢󠁳󠁣󠁴󠁿":"flag: Scotland","🏴󠁧󠁢󠁷󠁬󠁳󠁿":"flag: Wales"}},"Food & Drink":{dishware:{"🍴":"fork and knife","🍽":"fork and knife with plate","🏺":"amphora","🔪":"kitchen knife","🥄":"spoon","🥢":"chopsticks"},drink:{"☕":"hot beverage","🍴":"fork and knife","🍵":"teacup without handle","🍶":"sake","🍷":"wine glass","🍸":"cocktail glass","🍹":"tropical drink","🍺":"beer mug","🍻":"clinking beer mugs","🍼":"baby bottle","🍽":"fork and knife with plate","🍾":"bottle with popping cork","🏺":"amphora","🔪":"kitchen knife","🥂":"clinking glasses","🥃":"tumbler glass","🥄":"spoon","🥛":"glass of milk","🥢":"chopsticks","🥤":"cup with straw","🧃":"beverage box","🧉":"mate","🧊":"ice"},"food-asian":{"☕":"hot beverage","🍘":"rice cracker","🍙":"rice ball","🍚":"cooked rice","🍛":"curry rice","🍜":"steaming bowl","🍝":"spaghetti","🍠":"roasted sweet potato","🍡":"dango","🍢":"oden","🍣":"sushi","🍤":"fried shrimp","🍥":"fish cake with swirl","🍦":"soft ice cream","🍧":"shaved ice","🍨":"ice cream","🍩":"doughnut","🍪":"cookie","🍫":"chocolate bar","🍬":"candy","🍭":"lollipop","🍮":"custard","🍯":"honey pot","🍰":"shortcake","🍱":"bento box","🍴":"fork and knife","🍵":"teacup without handle","🍶":"sake","🍷":"wine glass","🍸":"cocktail glass","🍹":"tropical drink","🍺":"beer mug","🍻":"clinking beer mugs","🍼":"baby bottle","🍽":"fork and knife with plate","🍾":"bottle with popping cork","🎂":"birthday cake","🏺":"amphora","🔪":"kitchen knife","🥂":"clinking glasses","🥃":"tumbler glass","🥄":"spoon","🥛":"glass of milk","🥟":"dumpling","🥠":"fortune cookie","🥡":"takeout box","🥢":"chopsticks","🥤":"cup with straw","🥧":"pie","🥮":"moon cake","🦀":"crab","🦐":"shrimp","🦑":"squid","🦞":"lobster","🦪":"oyster","🧁":"cupcake","🧃":"beverage box","🧉":"mate","🧊":"ice"},"food-fruit":{"☕":"hot beverage","🌭":"hot dog","🌮":"taco","🌯":"burrito","🌰":"chestnut","🌶":"hot pepper","🌽":"ear of corn","🍄":"mushroom","🍅":"tomato","🍆":"eggplant","🍇":"grapes","🍈":"melon","🍉":"watermelon","🍊":"tangerine","🍋":"lemon","🍌":"banana","🍍":"pineapple","🍎":"red apple","🍏":"green apple","🍐":"pear","🍑":"peach","🍒":"cherries","🍓":"strawberry","🍔":"hamburger","🍕":"pizza","🍖":"meat on bone","🍗":"poultry leg","🍘":"rice cracker","🍙":"rice ball","🍚":"cooked rice","🍛":"curry rice","🍜":"steaming bowl","🍝":"spaghetti","🍞":"bread","🍟":"french fries","🍠":"roasted sweet potato","🍡":"dango","🍢":"oden","🍣":"sushi","🍤":"fried shrimp","🍥":"fish cake with swirl","🍦":"soft ice cream","🍧":"shaved ice","🍨":"ice cream","🍩":"doughnut","🍪":"cookie","🍫":"chocolate bar","🍬":"candy","🍭":"lollipop","🍮":"custard","🍯":"honey pot","🍰":"shortcake","🍱":"bento box","🍲":"pot of food","🍳":"cooking","🍴":"fork and knife","🍵":"teacup without handle","🍶":"sake","🍷":"wine glass","🍸":"cocktail glass","🍹":"tropical drink","🍺":"beer mug","🍻":"clinking beer mugs","🍼":"baby bottle","🍽":"fork and knife with plate","🍾":"bottle with popping cork","🍿":"popcorn","🎂":"birthday cake","🏺":"amphora","🔪":"kitchen knife","🥂":"clinking glasses","🥃":"tumbler glass","🥄":"spoon","🥐":"croissant","🥑":"avocado","🥒":"cucumber","🥓":"bacon","🥔":"potato","🥕":"carrot","🥖":"baguette bread","🥗":"green salad","🥘":"shallow pan of food","🥙":"stuffed flatbread","🥚":"egg","🥛":"glass of milk","🥜":"peanuts","🥝":"kiwi fruit","🥞":"pancakes","🥟":"dumpling","🥠":"fortune cookie","🥡":"takeout box","🥢":"chopsticks","🥣":"bowl with spoon","🥤":"cup with straw","🥥":"coconut","🥦":"broccoli","🥧":"pie","🥨":"pretzel","🥩":"cut of meat","🥪":"sandwich","🥫":"canned food","🥬":"leafy green","🥭":"mango","🥮":"moon cake","🥯":"bagel","🦀":"crab","🦐":"shrimp","🦑":"squid","🦞":"lobster","🦪":"oyster","🧀":"cheese wedge","🧁":"cupcake","🧂":"salt","🧃":"beverage box","🧄":"garlic","🧅":"onion","🧆":"falafel","🧇":"waffle","🧈":"butter","🧉":"mate","🧊":"ice"},"food-marine":{"☕":"hot beverage","🍦":"soft ice cream","🍧":"shaved ice","🍨":"ice cream","🍩":"doughnut","🍪":"cookie","🍫":"chocolate bar","🍬":"candy","🍭":"lollipop","🍮":"custard","🍯":"honey pot","🍰":"shortcake","🍴":"fork and knife","🍵":"teacup without handle","🍶":"sake","🍷":"wine glass","🍸":"cocktail glass","🍹":"tropical drink","🍺":"beer mug","🍻":"clinking beer mugs","🍼":"baby bottle","🍽":"fork and knife with plate","🍾":"bottle with popping cork","🎂":"birthday cake","🏺":"amphora","🔪":"kitchen knife","🥂":"clinking glasses","🥃":"tumbler glass","🥄":"spoon","🥛":"glass of milk","🥢":"chopsticks","🥤":"cup with straw","🥧":"pie","🦀":"crab","🦐":"shrimp","🦑":"squid","🦞":"lobster","🦪":"oyster","🧁":"cupcake","🧃":"beverage box","🧉":"mate","🧊":"ice"},"food-prepared":{"☕":"hot beverage","🌭":"hot dog","🌮":"taco","🌯":"burrito","🍔":"hamburger","🍕":"pizza","🍖":"meat on bone","🍗":"poultry leg","🍘":"rice cracker","🍙":"rice ball","🍚":"cooked rice","🍛":"curry rice","🍜":"steaming bowl","🍝":"spaghetti","🍞":"bread","🍟":"french fries","🍠":"roasted sweet potato","🍡":"dango","🍢":"oden","🍣":"sushi","🍤":"fried shrimp","🍥":"fish cake with swirl","🍦":"soft ice cream","🍧":"shaved ice","🍨":"ice cream","🍩":"doughnut","🍪":"cookie","🍫":"chocolate bar","🍬":"candy","🍭":"lollipop","🍮":"custard","🍯":"honey pot","🍰":"shortcake","🍱":"bento box","🍲":"pot of food","🍳":"cooking","🍴":"fork and knife","🍵":"teacup without handle","🍶":"sake","🍷":"wine glass","🍸":"cocktail glass","🍹":"tropical drink","🍺":"beer mug","🍻":"clinking beer mugs","🍼":"baby bottle","🍽":"fork and knife with plate","🍾":"bottle with popping cork","🍿":"popcorn","🎂":"birthday cake","🏺":"amphora","🔪":"kitchen knife","🥂":"clinking glasses","🥃":"tumbler glass","🥄":"spoon","🥐":"croissant","🥓":"bacon","🥖":"baguette bread","🥗":"green salad","🥘":"shallow pan of food","🥙":"stuffed flatbread","🥚":"egg","🥛":"glass of milk","🥞":"pancakes","🥟":"dumpling","🥠":"fortune cookie","🥡":"takeout box","🥢":"chopsticks","🥣":"bowl with spoon","🥤":"cup with straw","🥧":"pie","🥨":"pretzel","🥩":"cut of meat","🥪":"sandwich","🥫":"canned food","🥮":"moon cake","🥯":"bagel","🦀":"crab","🦐":"shrimp","🦑":"squid","🦞":"lobster","🦪":"oyster","🧀":"cheese wedge","🧁":"cupcake","🧂":"salt","🧃":"beverage box","🧆":"falafel","🧇":"waffle","🧈":"butter","🧉":"mate","🧊":"ice"},"food-sweet":{"☕":"hot beverage","🍦":"soft ice cream","🍧":"shaved ice","🍨":"ice cream","🍩":"doughnut","🍪":"cookie","🍫":"chocolate bar","🍬":"candy","🍭":"lollipop","🍮":"custard","🍯":"honey pot","🍰":"shortcake","🍴":"fork and knife","🍵":"teacup without handle","🍶":"sake","🍷":"wine glass","🍸":"cocktail glass","🍹":"tropical drink","🍺":"beer mug","🍻":"clinking beer mugs","🍼":"baby bottle","🍽":"fork and knife with plate","🍾":"bottle with popping cork","🎂":"birthday cake","🏺":"amphora","🔪":"kitchen knife","🥂":"clinking glasses","🥃":"tumbler glass","🥄":"spoon","🥛":"glass of milk","🥢":"chopsticks","🥤":"cup with straw","🥧":"pie","🧁":"cupcake","🧃":"beverage box","🧉":"mate","🧊":"ice"},"food-vegetable":{"☕":"hot beverage","🌭":"hot dog","🌮":"taco","🌯":"burrito","🌰":"chestnut","🌶":"hot pepper","🌽":"ear of corn","🍄":"mushroom","🍆":"eggplant","🍔":"hamburger","🍕":"pizza","🍖":"meat on bone","🍗":"poultry leg","🍘":"rice cracker","🍙":"rice ball","🍚":"cooked rice","🍛":"curry rice","🍜":"steaming bowl","🍝":"spaghetti","🍞":"bread","🍟":"french fries","🍠":"roasted sweet potato","🍡":"dango","🍢":"oden","🍣":"sushi","🍤":"fried shrimp","🍥":"fish cake with swirl","🍦":"soft ice cream","🍧":"shaved ice","🍨":"ice cream","🍩":"doughnut","🍪":"cookie","🍫":"chocolate bar","🍬":"candy","🍭":"lollipop","🍮":"custard","🍯":"honey pot","🍰":"shortcake","🍱":"bento box","🍲":"pot of food","🍳":"cooking","🍴":"fork and knife","🍵":"teacup without handle","🍶":"sake","🍷":"wine glass","🍸":"cocktail glass","🍹":"tropical drink","🍺":"beer mug","🍻":"clinking beer mugs","🍼":"baby bottle","🍽":"fork and knife with plate","🍾":"bottle with popping cork","🍿":"popcorn","🎂":"birthday cake","🏺":"amphora","🔪":"kitchen knife","🥂":"clinking glasses","🥃":"tumbler glass","🥄":"spoon","🥐":"croissant","🥑":"avocado","🥒":"cucumber","🥓":"bacon","🥔":"potato","🥕":"carrot","🥖":"baguette bread","🥗":"green salad","🥘":"shallow pan of food","🥙":"stuffed flatbread","🥚":"egg","🥛":"glass of milk","🥜":"peanuts","🥞":"pancakes","🥟":"dumpling","🥠":"fortune cookie","🥡":"takeout box","🥢":"chopsticks","🥣":"bowl with spoon","🥤":"cup with straw","🥦":"broccoli","🥧":"pie","🥨":"pretzel","🥩":"cut of meat","🥪":"sandwich","🥫":"canned food","🥬":"leafy green","🥮":"moon cake","🥯":"bagel","🦀":"crab","🦐":"shrimp","🦑":"squid","🦞":"lobster","🦪":"oyster","🧀":"cheese wedge","🧁":"cupcake","🧂":"salt","🧃":"beverage box","🧄":"garlic","🧅":"onion","🧆":"falafel","🧇":"waffle","🧈":"butter","🧉":"mate","🧊":"ice"}},Objects:{"book-paper":{"⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🏷":"label","🏹":"bow and arrow","💉":"syringe","💊":"pill","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💼":"briefcase","📁":"file folder","📂":"open file folder","📃":"page with curl","📄":"page facing up","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📑":"bookmark tabs","📒":"ledger","📓":"notebook","📔":"notebook with decorative cover","📕":"closed book","📖":"open book","📗":"green book","📘":"blue book","📙":"orange book","📚":"books","📜":"scroll","📝":"memo","📡":"satellite antenna","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","📰":"newspaper","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔖":"bookmark","🔗":"link","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗞":"rolled-up newspaper","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe"},clothing:{"⌨":"keyboard","☎":"telephone","⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛑":"rescue worker’s helmet","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🎒":"backpack","🎓":"graduation cap","🎙":"studio microphone","🎚":"level slider","🎛":"control knobs","🎞":"film frames","🎤":"microphone","🎥":"movie camera","🎧":"headphone","🎩":"top hat","🎬":"clapper board","🎵":"musical note","🎶":"musical notes","🎷":"saxophone","🎸":"guitar","🎹":"musical keyboard","🎺":"trumpet","🎻":"violin","🎼":"musical score","🏮":"red paper lantern","🏷":"label","🏹":"bow and arrow","👑":"crown","👒":"woman’s hat","👓":"glasses","👔":"necktie","👕":"t-shirt","👖":"jeans","👗":"dress","👘":"kimono","👙":"bikini","👚":"woman’s clothes","👛":"purse","👜":"handbag","👝":"clutch bag","👞":"man’s shoe","👟":"running shoe","👠":"high-heeled shoe","👡":"woman’s sandal","👢":"woman’s boot","💄":"lipstick","💉":"syringe","💊":"pill","💍":"ring","💎":"gem stone","💡":"light bulb","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💻":"laptop","💼":"briefcase","💽":"computer disk","💾":"floppy disk","💿":"optical disk","📀":"dvd","📁":"file folder","📂":"open file folder","📃":"page with curl","📄":"page facing up","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📑":"bookmark tabs","📒":"ledger","📓":"notebook","📔":"notebook with decorative cover","📕":"closed book","📖":"open book","📗":"green book","📘":"blue book","📙":"orange book","📚":"books","📜":"scroll","📝":"memo","📞":"telephone receiver","📟":"pager","📠":"fax machine","📡":"satellite antenna","📢":"loudspeaker","📣":"megaphone","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","📯":"postal horn","📰":"newspaper","📱":"mobile phone","📲":"mobile phone with arrow","📷":"camera","📸":"camera with flash","📹":"video camera","📺":"television","📻":"radio","📼":"videocassette","📽":"film projector","📿":"prayer beads","🔇":"muted speaker","🔈":"speaker low volume","🔉":"speaker medium volume","🔊":"speaker high volume","🔋":"battery","🔌":"electric plug","🔍":"magnifying glass tilted left","🔎":"magnifying glass tilted right","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔔":"bell","🔕":"bell with slash","🔖":"bookmark","🔗":"link","🔦":"flashlight","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🕯":"candle","🕶":"sunglasses","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🖥":"desktop computer","🖨":"printer","🖱":"computer mouse","🖲":"trackball","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗞":"rolled-up newspaper","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛍":"shopping bags","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🥁":"drum","🥻":"sari","🥼":"lab coat","🥽":"goggles","🥾":"hiking boot","🥿":"flat shoe","🦯":"white cane","🦺":"safety vest","🧢":"billed cap","🧣":"scarf","🧤":"gloves","🧥":"coat","🧦":"socks","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧮":"abacus","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩰":"ballet shoes","🩱":"one-piece swimsuit","🩲":"briefs","🩳":"shorts","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe","🪔":"diya lamp","🪕":"banjo"},computer:{"⌨":"keyboard","⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🎞":"film frames","🎥":"movie camera","🎬":"clapper board","🏮":"red paper lantern","🏷":"label","🏹":"bow and arrow","💉":"syringe","💊":"pill","💡":"light bulb","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💻":"laptop","💼":"briefcase","💽":"computer disk","💾":"floppy disk","💿":"optical disk","📀":"dvd","📁":"file folder","📂":"open file folder","📃":"page with curl","📄":"page facing up","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📑":"bookmark tabs","📒":"ledger","📓":"notebook","📔":"notebook with decorative cover","📕":"closed book","📖":"open book","📗":"green book","📘":"blue book","📙":"orange book","📚":"books","📜":"scroll","📝":"memo","📡":"satellite antenna","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","📰":"newspaper","📷":"camera","📸":"camera with flash","📹":"video camera","📺":"television","📼":"videocassette","📽":"film projector","🔋":"battery","🔌":"electric plug","🔍":"magnifying glass tilted left","🔎":"magnifying glass tilted right","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔖":"bookmark","🔗":"link","🔦":"flashlight","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🕯":"candle","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🖥":"desktop computer","🖨":"printer","🖱":"computer mouse","🖲":"trackball","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗞":"rolled-up newspaper","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧮":"abacus","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe","🪔":"diya lamp"},household:{"⚰":"coffin","⚱":"funeral urn","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🧯":"fire extinguisher","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🪑":"chair","🪒":"razor"},"light & video":{"⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🎞":"film frames","🎥":"movie camera","🎬":"clapper board","🏮":"red paper lantern","🏷":"label","🏹":"bow and arrow","💉":"syringe","💊":"pill","💡":"light bulb","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💼":"briefcase","📁":"file folder","📂":"open file folder","📃":"page with curl","📄":"page facing up","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📑":"bookmark tabs","📒":"ledger","📓":"notebook","📔":"notebook with decorative cover","📕":"closed book","📖":"open book","📗":"green book","📘":"blue book","📙":"orange book","📚":"books","📜":"scroll","📝":"memo","📡":"satellite antenna","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","📰":"newspaper","📷":"camera","📸":"camera with flash","📹":"video camera","📺":"television","📼":"videocassette","📽":"film projector","🔍":"magnifying glass tilted left","🔎":"magnifying glass tilted right","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔖":"bookmark","🔗":"link","🔦":"flashlight","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🕯":"candle","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗞":"rolled-up newspaper","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe","🪔":"diya lamp"},lock:{"⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","🏹":"bow and arrow","💉":"syringe","💊":"pill","📡":"satellite antenna","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔗":"link","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🗜":"clamp","🗝":"old key","🗡":"dagger","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe"},mail:{"⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🏹":"bow and arrow","💉":"syringe","💊":"pill","💼":"briefcase","📁":"file folder","📂":"open file folder","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📝":"memo","📡":"satellite antenna","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔗":"link","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe"},medical:{"⚰":"coffin","⚱":"funeral urn","💉":"syringe","💊":"pill","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🧯":"fire extinguisher","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor"},money:{"⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🏹":"bow and arrow","💉":"syringe","💊":"pill","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💼":"briefcase","📁":"file folder","📂":"open file folder","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📝":"memo","📡":"satellite antenna","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔗":"link","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe"},music:{"⌨":"keyboard","☎":"telephone","⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🎙":"studio microphone","🎚":"level slider","🎛":"control knobs","🎞":"film frames","🎤":"microphone","🎥":"movie camera","🎧":"headphone","🎬":"clapper board","🎵":"musical note","🎶":"musical notes","🎷":"saxophone","🎸":"guitar","🎹":"musical keyboard","🎺":"trumpet","🎻":"violin","🎼":"musical score","🏮":"red paper lantern","🏷":"label","🏹":"bow and arrow","💉":"syringe","💊":"pill","💡":"light bulb","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💻":"laptop","💼":"briefcase","💽":"computer disk","💾":"floppy disk","💿":"optical disk","📀":"dvd","📁":"file folder","📂":"open file folder","📃":"page with curl","📄":"page facing up","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📑":"bookmark tabs","📒":"ledger","📓":"notebook","📔":"notebook with decorative cover","📕":"closed book","📖":"open book","📗":"green book","📘":"blue book","📙":"orange book","📚":"books","📜":"scroll","📝":"memo","📞":"telephone receiver","📟":"pager","📠":"fax machine","📡":"satellite antenna","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","📰":"newspaper","📱":"mobile phone","📲":"mobile phone with arrow","📷":"camera","📸":"camera with flash","📹":"video camera","📺":"television","📻":"radio","📼":"videocassette","📽":"film projector","🔋":"battery","🔌":"electric plug","🔍":"magnifying glass tilted left","🔎":"magnifying glass tilted right","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔖":"bookmark","🔗":"link","🔦":"flashlight","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🕯":"candle","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🖥":"desktop computer","🖨":"printer","🖱":"computer mouse","🖲":"trackball","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗞":"rolled-up newspaper","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🥁":"drum","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧮":"abacus","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe","🪔":"diya lamp","🪕":"banjo"},"musical-instrument":{"⌨":"keyboard","☎":"telephone","⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🎞":"film frames","🎥":"movie camera","🎬":"clapper board","🎷":"saxophone","🎸":"guitar","🎹":"musical keyboard","🎺":"trumpet","🎻":"violin","🏮":"red paper lantern","🏷":"label","🏹":"bow and arrow","💉":"syringe","💊":"pill","💡":"light bulb","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💻":"laptop","💼":"briefcase","💽":"computer disk","💾":"floppy disk","💿":"optical disk","📀":"dvd","📁":"file folder","📂":"open file folder","📃":"page with curl","📄":"page facing up","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📑":"bookmark tabs","📒":"ledger","📓":"notebook","📔":"notebook with decorative cover","📕":"closed book","📖":"open book","📗":"green book","📘":"blue book","📙":"orange book","📚":"books","📜":"scroll","📝":"memo","📞":"telephone receiver","📟":"pager","📠":"fax machine","📡":"satellite antenna","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","📰":"newspaper","📱":"mobile phone","📲":"mobile phone with arrow","📷":"camera","📸":"camera with flash","📹":"video camera","📺":"television","📼":"videocassette","📽":"film projector","🔋":"battery","🔌":"electric plug","🔍":"magnifying glass tilted left","🔎":"magnifying glass tilted right","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔖":"bookmark","🔗":"link","🔦":"flashlight","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🕯":"candle","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🖥":"desktop computer","🖨":"printer","🖱":"computer mouse","🖲":"trackball","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗞":"rolled-up newspaper","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🥁":"drum","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧮":"abacus","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe","🪔":"diya lamp","🪕":"banjo"},office:{"⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","🏹":"bow and arrow","💉":"syringe","💊":"pill","💼":"briefcase","📁":"file folder","📂":"open file folder","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📡":"satellite antenna","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔗":"link","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🖇":"linked paperclips","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗡":"dagger","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe"},"other-object":{"⚰":"coffin","⚱":"funeral urn","🗿":"moai","🚬":"cigarette"},phone:{"⌨":"keyboard","☎":"telephone","⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🎞":"film frames","🎥":"movie camera","🎬":"clapper board","🏮":"red paper lantern","🏷":"label","🏹":"bow and arrow","💉":"syringe","💊":"pill","💡":"light bulb","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💻":"laptop","💼":"briefcase","💽":"computer disk","💾":"floppy disk","💿":"optical disk","📀":"dvd","📁":"file folder","📂":"open file folder","📃":"page with curl","📄":"page facing up","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📑":"bookmark tabs","📒":"ledger","📓":"notebook","📔":"notebook with decorative cover","📕":"closed book","📖":"open book","📗":"green book","📘":"blue book","📙":"orange book","📚":"books","📜":"scroll","📝":"memo","📞":"telephone receiver","📟":"pager","📠":"fax machine","📡":"satellite antenna","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","📰":"newspaper","📱":"mobile phone","📲":"mobile phone with arrow","📷":"camera","📸":"camera with flash","📹":"video camera","📺":"television","📼":"videocassette","📽":"film projector","🔋":"battery","🔌":"electric plug","🔍":"magnifying glass tilted left","🔎":"magnifying glass tilted right","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔖":"bookmark","🔗":"link","🔦":"flashlight","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🕯":"candle","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🖥":"desktop computer","🖨":"printer","🖱":"computer mouse","🖲":"trackball","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗞":"rolled-up newspaper","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧮":"abacus","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe","🪔":"diya lamp"},science:{"⚗":"alembic","⚰":"coffin","⚱":"funeral urn","💉":"syringe","💊":"pill","📡":"satellite antenna","🔬":"microscope","🔭":"telescope","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor"},sound:{"⌨":"keyboard","☎":"telephone","⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✉":"envelope","✏":"pencil","✒":"black nib","🎙":"studio microphone","🎚":"level slider","🎛":"control knobs","🎞":"film frames","🎤":"microphone","🎥":"movie camera","🎧":"headphone","🎬":"clapper board","🎵":"musical note","🎶":"musical notes","🎷":"saxophone","🎸":"guitar","🎹":"musical keyboard","🎺":"trumpet","🎻":"violin","🎼":"musical score","🏮":"red paper lantern","🏷":"label","🏹":"bow and arrow","💉":"syringe","💊":"pill","💡":"light bulb","💰":"money bag","💳":"credit card","💴":"yen banknote","💵":"dollar banknote","💶":"euro banknote","💷":"pound banknote","💸":"money with wings","💹":"chart increasing with yen","💻":"laptop","💼":"briefcase","💽":"computer disk","💾":"floppy disk","💿":"optical disk","📀":"dvd","📁":"file folder","📂":"open file folder","📃":"page with curl","📄":"page facing up","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📑":"bookmark tabs","📒":"ledger","📓":"notebook","📔":"notebook with decorative cover","📕":"closed book","📖":"open book","📗":"green book","📘":"blue book","📙":"orange book","📚":"books","📜":"scroll","📝":"memo","📞":"telephone receiver","📟":"pager","📠":"fax machine","📡":"satellite antenna","📢":"loudspeaker","📣":"megaphone","📤":"outbox tray","📥":"inbox tray","📦":"package","📧":"e-mail","📨":"incoming envelope","📩":"envelope with arrow","📪":"closed mailbox with lowered flag","📫":"closed mailbox with raised flag","📬":"open mailbox with raised flag","📭":"open mailbox with lowered flag","📮":"postbox","📯":"postal horn","📰":"newspaper","📱":"mobile phone","📲":"mobile phone with arrow","📷":"camera","📸":"camera with flash","📹":"video camera","📺":"television","📻":"radio","📼":"videocassette","📽":"film projector","🔇":"muted speaker","🔈":"speaker low volume","🔉":"speaker medium volume","🔊":"speaker high volume","🔋":"battery","🔌":"electric plug","🔍":"magnifying glass tilted left","🔎":"magnifying glass tilted right","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔔":"bell","🔕":"bell with slash","🔖":"bookmark","🔗":"link","🔦":"flashlight","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🕯":"candle","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🖥":"desktop computer","🖨":"printer","🖱":"computer mouse","🖲":"trackball","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗞":"rolled-up newspaper","🗡":"dagger","🗳":"ballot box with ballot","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🥁":"drum","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧮":"abacus","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🧾":"receipt","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe","🪔":"diya lamp","🪕":"banjo"},tool:{"⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","🏹":"bow and arrow","💉":"syringe","💊":"pill","📡":"satellite antenna","🔗":"link","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🗜":"clamp","🗡":"dagger","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe"},writing:{"⚒":"hammer and pick","⚔":"crossed swords","⚖":"balance scale","⚗":"alembic","⚙":"gear","⚰":"coffin","⚱":"funeral urn","⛏":"pick","⛓":"chains","✂":"scissors","✏":"pencil","✒":"black nib","🏹":"bow and arrow","💉":"syringe","💊":"pill","💼":"briefcase","📁":"file folder","📂":"open file folder","📅":"calendar","📆":"tear-off calendar","📇":"card index","📈":"chart increasing","📉":"chart decreasing","📊":"bar chart","📋":"clipboard","📌":"pushpin","📍":"round pushpin","📎":"paperclip","📏":"straight ruler","📐":"triangular ruler","📝":"memo","📡":"satellite antenna","🔏":"locked with pen","🔐":"locked with key","🔑":"key","🔒":"locked","🔓":"unlocked","🔗":"link","🔧":"wrench","🔨":"hammer","🔩":"nut and bolt","🔫":"pistol","🔬":"microscope","🔭":"telescope","🖇":"linked paperclips","🖊":"pen","🖋":"fountain pen","🖌":"paintbrush","🖍":"crayon","🗂":"card index dividers","🗃":"card file box","🗄":"file cabinet","🗑":"wastebasket","🗒":"spiral notepad","🗓":"spiral calendar","🗜":"clamp","🗝":"old key","🗡":"dagger","🗿":"moai","🚪":"door","🚬":"cigarette","🚽":"toilet","🚿":"shower","🛁":"bathtub","🛋":"couch and lamp","🛏":"bed","🛒":"shopping cart","🛠":"hammer and wrench","🛡":"shield","🦯":"white cane","🧪":"test tube","🧫":"petri dish","🧬":"dna","🧯":"fire extinguisher","🧰":"toolbox","🧲":"magnet","🧴":"lotion bottle","🧷":"safety pin","🧹":"broom","🧺":"basket","🧻":"roll of paper","🧼":"soap","🧽":"sponge","🩸":"drop of blood","🩹":"adhesive bandage","🩺":"stethoscope","🪑":"chair","🪒":"razor","🪓":"axe"}},"People & Body":{"body-parts":{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👀":"eyes","👁":"eye","👂":"ear","👃":"nose","👄":"mouth","👅":"tongue","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👦":"boy","👧":"girl","👨":"man","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦰":"man: red hair","👨‍🦱":"man: curly hair","👨‍🦲":"man: bald","👨‍🦳":"man: white hair","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩":"woman","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦰":"woman: red hair","👩‍🦱":"woman: curly hair","👩‍🦲":"woman: bald","👩‍🦳":"woman: white hair","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👱":"person: blond hair","👱‍♀️":"woman: blond hair","👱‍♂️":"man: blond hair","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👴":"old man","👵":"old woman","👶":"baby","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","💪":"flexed biceps","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦴":"bone","🦵":"leg","🦶":"foot","🦷":"tooth","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🦻":"ear with hearing aid","🦾":"mechanical arm","🦿":"mechanical leg","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑":"person","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦰":"⊛ person: red hair","🧑‍🦱":"⊛ person: curly hair","🧑‍🦲":"⊛ person: bald","🧑‍🦳":"⊛ person: white hair","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧒":"child","🧓":"older person","🧔":"man: beard","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie","🧠":"brain"},family:{"👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","💏":"kiss","💑":"couple with heart","🗣":"speaking head","🧑‍🤝‍🧑":"people holding hands"},"hand-fingers-closed":{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","✊":"raised fist","✍":"writing hand","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👀":"eyes","👁":"eye","👂":"ear","👃":"nose","👄":"mouth","👅":"tongue","👊":"oncoming fist","👍":"thumbs up","👎":"thumbs down","👏":"clapping hands","👐":"open hands","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👦":"boy","👧":"girl","👨":"man","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦰":"man: red hair","👨‍🦱":"man: curly hair","👨‍🦲":"man: bald","👨‍🦳":"man: white hair","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩":"woman","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦰":"woman: red hair","👩‍🦱":"woman: curly hair","👩‍🦲":"woman: bald","👩‍🦳":"woman: white hair","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👱":"person: blond hair","👱‍♀️":"woman: blond hair","👱‍♂️":"man: blond hair","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👴":"old man","👵":"old woman","👶":"baby","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💅":"nail polish","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","💪":"flexed biceps","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙌":"raising hands","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🙏":"folded hands","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤛":"left-facing fist","🤜":"right-facing fist","🤝":"handshake","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤲":"palms up together","🤳":"selfie","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦴":"bone","🦵":"leg","🦶":"foot","🦷":"tooth","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🦻":"ear with hearing aid","🦾":"mechanical arm","🦿":"mechanical leg","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑":"person","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦰":"⊛ person: red hair","🧑‍🦱":"⊛ person: curly hair","🧑‍🦲":"⊛ person: bald","🧑‍🦳":"⊛ person: white hair","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧒":"child","🧓":"older person","🧔":"man: beard","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie","🧠":"brain"},"hand-fingers-open":{"☝":"index pointing up","⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","✊":"raised fist","✋":"raised hand","✌":"victory hand","✍":"writing hand","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👀":"eyes","👁":"eye","👂":"ear","👃":"nose","👄":"mouth","👅":"tongue","👆":"backhand index pointing up","👇":"backhand index pointing down","👈":"backhand index pointing left","👉":"backhand index pointing right","👊":"oncoming fist","👋":"waving hand","👌":"OK hand","👍":"thumbs up","👎":"thumbs down","👏":"clapping hands","👐":"open hands","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👦":"boy","👧":"girl","👨":"man","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦰":"man: red hair","👨‍🦱":"man: curly hair","👨‍🦲":"man: bald","👨‍🦳":"man: white hair","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩":"woman","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦰":"woman: red hair","👩‍🦱":"woman: curly hair","👩‍🦲":"woman: bald","👩‍🦳":"woman: white hair","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👱":"person: blond hair","👱‍♀️":"woman: blond hair","👱‍♂️":"man: blond hair","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👴":"old man","👵":"old woman","👶":"baby","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💅":"nail polish","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","💪":"flexed biceps","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🖐":"hand with fingers splayed","🖕":"middle finger","🖖":"vulcan salute","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙌":"raising hands","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🙏":"folded hands","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤏":"pinching hand","🤘":"sign of the horns","🤙":"call me hand","🤚":"raised back of hand","🤛":"left-facing fist","🤜":"right-facing fist","🤝":"handshake","🤞":"crossed fingers","🤟":"love-you gesture","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤲":"palms up together","🤳":"selfie","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦴":"bone","🦵":"leg","🦶":"foot","🦷":"tooth","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🦻":"ear with hearing aid","🦾":"mechanical arm","🦿":"mechanical leg","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑":"person","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦰":"⊛ person: red hair","🧑‍🦱":"⊛ person: curly hair","🧑‍🦲":"⊛ person: bald","🧑‍🦳":"⊛ person: white hair","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧒":"child","🧓":"older person","🧔":"man: beard","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie","🧠":"brain"},"hand-fingers-partial":{"☝":"index pointing up","⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","✊":"raised fist","✌":"victory hand","✍":"writing hand","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👀":"eyes","👁":"eye","👂":"ear","👃":"nose","👄":"mouth","👅":"tongue","👆":"backhand index pointing up","👇":"backhand index pointing down","👈":"backhand index pointing left","👉":"backhand index pointing right","👊":"oncoming fist","👌":"OK hand","👍":"thumbs up","👎":"thumbs down","👏":"clapping hands","👐":"open hands","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👦":"boy","👧":"girl","👨":"man","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦰":"man: red hair","👨‍🦱":"man: curly hair","👨‍🦲":"man: bald","👨‍🦳":"man: white hair","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩":"woman","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦰":"woman: red hair","👩‍🦱":"woman: curly hair","👩‍🦲":"woman: bald","👩‍🦳":"woman: white hair","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👱":"person: blond hair","👱‍♀️":"woman: blond hair","👱‍♂️":"man: blond hair","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👴":"old man","👵":"old woman","👶":"baby","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💅":"nail polish","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","💪":"flexed biceps","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🖕":"middle finger","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙌":"raising hands","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🙏":"folded hands","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤏":"pinching hand","🤘":"sign of the horns","🤙":"call me hand","🤛":"left-facing fist","🤜":"right-facing fist","🤝":"handshake","🤞":"crossed fingers","🤟":"love-you gesture","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤲":"palms up together","🤳":"selfie","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦴":"bone","🦵":"leg","🦶":"foot","🦷":"tooth","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🦻":"ear with hearing aid","🦾":"mechanical arm","🦿":"mechanical leg","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑":"person","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦰":"⊛ person: red hair","🧑‍🦱":"⊛ person: curly hair","🧑‍🦲":"⊛ person: bald","🧑‍🦳":"⊛ person: white hair","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧒":"child","🧓":"older person","🧔":"man: beard","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie","🧠":"brain"},"hand-prop":{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","✍":"writing hand","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👀":"eyes","👁":"eye","👂":"ear","👃":"nose","👄":"mouth","👅":"tongue","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👦":"boy","👧":"girl","👨":"man","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦰":"man: red hair","👨‍🦱":"man: curly hair","👨‍🦲":"man: bald","👨‍🦳":"man: white hair","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩":"woman","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦰":"woman: red hair","👩‍🦱":"woman: curly hair","👩‍🦲":"woman: bald","👩‍🦳":"woman: white hair","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👱":"person: blond hair","👱‍♀️":"woman: blond hair","👱‍♂️":"man: blond hair","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👴":"old man","👵":"old woman","👶":"baby","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💅":"nail polish","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","💪":"flexed biceps","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤳":"selfie","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦴":"bone","🦵":"leg","🦶":"foot","🦷":"tooth","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🦻":"ear with hearing aid","🦾":"mechanical arm","🦿":"mechanical leg","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑":"person","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦰":"⊛ person: red hair","🧑‍🦱":"⊛ person: curly hair","🧑‍🦲":"⊛ person: bald","🧑‍🦳":"⊛ person: white hair","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧒":"child","🧓":"older person","🧔":"man: beard","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie","🧠":"brain"},"hand-single-finger":{"☝":"index pointing up","⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","✊":"raised fist","✍":"writing hand","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👀":"eyes","👁":"eye","👂":"ear","👃":"nose","👄":"mouth","👅":"tongue","👆":"backhand index pointing up","👇":"backhand index pointing down","👈":"backhand index pointing left","👉":"backhand index pointing right","👊":"oncoming fist","👍":"thumbs up","👎":"thumbs down","👏":"clapping hands","👐":"open hands","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👦":"boy","👧":"girl","👨":"man","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦰":"man: red hair","👨‍🦱":"man: curly hair","👨‍🦲":"man: bald","👨‍🦳":"man: white hair","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩":"woman","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦰":"woman: red hair","👩‍🦱":"woman: curly hair","👩‍🦲":"woman: bald","👩‍🦳":"woman: white hair","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👱":"person: blond hair","👱‍♀️":"woman: blond hair","👱‍♂️":"man: blond hair","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👴":"old man","👵":"old woman","👶":"baby","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💅":"nail polish","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","💪":"flexed biceps","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🖕":"middle finger","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙌":"raising hands","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🙏":"folded hands","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤛":"left-facing fist","🤜":"right-facing fist","🤝":"handshake","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤲":"palms up together","🤳":"selfie","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦴":"bone","🦵":"leg","🦶":"foot","🦷":"tooth","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🦻":"ear with hearing aid","🦾":"mechanical arm","🦿":"mechanical leg","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑":"person","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦰":"⊛ person: red hair","🧑‍🦱":"⊛ person: curly hair","🧑‍🦲":"⊛ person: bald","🧑‍🦳":"⊛ person: white hair","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧒":"child","🧓":"older person","🧔":"man: beard","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie","🧠":"brain"},hands:{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","✍":"writing hand","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👀":"eyes","👁":"eye","👂":"ear","👃":"nose","👄":"mouth","👅":"tongue","👏":"clapping hands","👐":"open hands","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👦":"boy","👧":"girl","👨":"man","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦰":"man: red hair","👨‍🦱":"man: curly hair","👨‍🦲":"man: bald","👨‍🦳":"man: white hair","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩":"woman","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦰":"woman: red hair","👩‍🦱":"woman: curly hair","👩‍🦲":"woman: bald","👩‍🦳":"woman: white hair","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👱":"person: blond hair","👱‍♀️":"woman: blond hair","👱‍♂️":"man: blond hair","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👴":"old man","👵":"old woman","👶":"baby","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💅":"nail polish","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","💪":"flexed biceps","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙌":"raising hands","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🙏":"folded hands","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤝":"handshake","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤲":"palms up together","🤳":"selfie","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦴":"bone","🦵":"leg","🦶":"foot","🦷":"tooth","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🦻":"ear with hearing aid","🦾":"mechanical arm","🦿":"mechanical leg","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑":"person","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦰":"⊛ person: red hair","🧑‍🦱":"⊛ person: curly hair","🧑‍🦲":"⊛ person: bald","🧑‍🦳":"⊛ person: white hair","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧒":"child","🧓":"older person","🧔":"man: beard","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie","🧠":"brain"},person:{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👦":"boy","👧":"girl","👨":"man","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦰":"man: red hair","👨‍🦱":"man: curly hair","👨‍🦲":"man: bald","👨‍🦳":"man: white hair","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩":"woman","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦰":"woman: red hair","👩‍🦱":"woman: curly hair","👩‍🦲":"woman: bald","👩‍🦳":"woman: white hair","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👱":"person: blond hair","👱‍♀️":"woman: blond hair","👱‍♂️":"man: blond hair","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👴":"old man","👵":"old woman","👶":"baby","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑":"person","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦰":"⊛ person: red hair","🧑‍🦱":"⊛ person: curly hair","🧑‍🦲":"⊛ person: bald","🧑‍🦳":"⊛ person: white hair","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧒":"child","🧓":"older person","🧔":"man: beard","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie"},"person-activity":{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍🦯":"man with white cane","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍🦯":"woman with white cane","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","💃":"woman dancing","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","🕴":"person in suit levitating","🕺":"man dancing","🗣":"speaking head","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position"},"person-fantasy":{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍🦯":"man with white cane","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍🦯":"woman with white cane","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👼":"baby angel","💃":"woman dancing","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","🕴":"person in suit levitating","🕺":"man dancing","🗣":"speaking head","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤶":"Mrs. Claus","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie"},"person-gesture":{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💁":"person tipping hand","💁‍♀️":"woman tipping hand","💁‍♂️":"man tipping hand","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🗣":"speaking head","🙅":"person gesturing NO","🙅‍♀️":"woman gesturing NO","🙅‍♂️":"man gesturing NO","🙆":"person gesturing OK","🙆‍♀️":"woman gesturing OK","🙆‍♂️":"man gesturing OK","🙇":"person bowing","🙇‍♀️":"woman bowing","🙇‍♂️":"man bowing","🙋":"person raising hand","🙋‍♀️":"woman raising hand","🙋‍♂️":"man raising hand","🙍":"person frowning","🙍‍♀️":"woman frowning","🙍‍♂️":"man frowning","🙎":"person pouting","🙎‍♀️":"woman pouting","🙎‍♂️":"man pouting","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤦":"person facepalming","🤦‍♀️":"woman facepalming","🤦‍♂️":"man facepalming","🤰":"pregnant woman","🤱":"breast-feeding","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤷":"person shrugging","🤷‍♀️":"woman shrugging","🤷‍♂️":"man shrugging","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧏":"deaf person","🧏‍♀️":"deaf woman","🧏‍♂️":"deaf man","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie"},"person-resting":{"👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","💏":"kiss","💑":"couple with heart","🗣":"speaking head","🛀":"person taking bath","🛌":"person in bed","🧑‍🤝‍🧑":"people holding hands","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position"},"person-role":{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","🎅":"Santa Claus","🏂":"snowboarder","🏃":"person running","🏃‍♀️":"woman running","🏃‍♂️":"man running","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👨‍⚕️":"man health worker","👨‍⚖️":"man judge","👨‍✈️":"man pilot","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍🌾":"man farmer","👨‍🍳":"man cook","👨‍🎓":"man student","👨‍🎤":"man singer","👨‍🎨":"man artist","👨‍🏫":"man teacher","👨‍🏭":"man factory worker","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👨‍💻":"man technologist","👨‍💼":"man office worker","👨‍🔧":"man mechanic","👨‍🔬":"man scientist","👨‍🚀":"man astronaut","👨‍🚒":"man firefighter","👨‍🦯":"man with white cane","👨‍🦼":"man in motorized wheelchair","👨‍🦽":"man in manual wheelchair","👩‍⚕️":"woman health worker","👩‍⚖️":"woman judge","👩‍✈️":"woman pilot","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍🌾":"woman farmer","👩‍🍳":"woman cook","👩‍🎓":"woman student","👩‍🎤":"woman singer","👩‍🎨":"woman artist","👩‍🏫":"woman teacher","👩‍🏭":"woman factory worker","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👩‍💻":"woman technologist","👩‍💼":"woman office worker","👩‍🔧":"woman mechanic","👩‍🔬":"woman scientist","👩‍🚀":"woman astronaut","👩‍🚒":"woman firefighter","👩‍🦯":"woman with white cane","👩‍🦼":"woman in motorized wheelchair","👩‍🦽":"woman in manual wheelchair","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","👮":"police officer","👮‍♀️":"woman police officer","👮‍♂️":"man police officer","👯":"people with bunny ears","👯‍♀️":"women with bunny ears","👯‍♂️":"men with bunny ears","👰":"person with veil","👲":"person with skullcap","👳":"person wearing turban","👳‍♀️":"woman wearing turban","👳‍♂️":"man wearing turban","👷":"construction worker","👷‍♀️":"woman construction worker","👷‍♂️":"man construction worker","👸":"princess","👼":"baby angel","💂":"guard","💂‍♀️":"woman guard","💂‍♂️":"man guard","💃":"woman dancing","💆":"person getting massage","💆‍♀️":"woman getting massage","💆‍♂️":"man getting massage","💇":"person getting haircut","💇‍♀️":"woman getting haircut","💇‍♂️":"man getting haircut","💏":"kiss","💑":"couple with heart","🕴":"person in suit levitating","🕵":"detective","🕵️‍♀️":"woman detective","🕵️‍♂️":"man detective","🕺":"man dancing","🗣":"speaking head","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🚶":"person walking","🚶‍♀️":"woman walking","🚶‍♂️":"man walking","🛀":"person taking bath","🛌":"person in bed","🤰":"pregnant woman","🤱":"breast-feeding","🤴":"prince","🤵":"person in tuxedo","🤶":"Mrs. Claus","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🦸":"superhero","🦸‍♀️":"woman superhero","🦸‍♂️":"man superhero","🦹":"supervillain","🦹‍♀️":"woman supervillain","🦹‍♂️":"man supervillain","🧍":"person standing","🧍‍♀️":"woman standing","🧍‍♂️":"man standing","🧎":"person kneeling","🧎‍♀️":"woman kneeling","🧎‍♂️":"man kneeling","🧑‍⚕️":"⊛ health worker","🧑‍⚖️":"⊛ judge","🧑‍✈️":"⊛ pilot","🧑‍🌾":"⊛ farmer","🧑‍🍳":"⊛ cook","🧑‍🎓":"⊛ student","🧑‍🎤":"⊛ singer","🧑‍🎨":"⊛ artist","🧑‍🏫":"⊛ teacher","🧑‍🏭":"⊛ factory worker","🧑‍💻":"⊛ technologist","🧑‍💼":"⊛ office worker","🧑‍🔧":"⊛ mechanic","🧑‍🔬":"⊛ scientist","🧑‍🚀":"⊛ astronaut","🧑‍🚒":"⊛ firefighter","🧑‍🤝‍🧑":"people holding hands","🧑‍🦯":"⊛ person with white cane","🧑‍🦼":"⊛ person in motorized wheelchair","🧑‍🦽":"⊛ person in manual wheelchair","🧕":"woman with headscarf","🧖":"person in steamy room","🧖‍♀️":"woman in steamy room","🧖‍♂️":"man in steamy room","🧗":"person climbing","🧗‍♀️":"woman climbing","🧗‍♂️":"man climbing","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position","🧙":"mage","🧙‍♀️":"woman mage","🧙‍♂️":"man mage","🧚":"fairy","🧚‍♀️":"woman fairy","🧚‍♂️":"man fairy","🧛":"vampire","🧛‍♀️":"woman vampire","🧛‍♂️":"man vampire","🧜":"merperson","🧜‍♀️":"mermaid","🧜‍♂️":"merman","🧝":"elf","🧝‍♀️":"woman elf","🧝‍♂️":"man elf","🧞":"genie","🧞‍♀️":"woman genie","🧞‍♂️":"man genie","🧟":"zombie","🧟‍♀️":"woman zombie","🧟‍♂️":"man zombie"},"person-sport":{"⛷":"skier","⛹":"person bouncing ball","⛹️‍♀️":"woman bouncing ball","⛹️‍♂️":"man bouncing ball","🏂":"snowboarder","🏄":"person surfing","🏄‍♀️":"woman surfing","🏄‍♂️":"man surfing","🏇":"horse racing","🏊":"person swimming","🏊‍♀️":"woman swimming","🏊‍♂️":"man swimming","🏋":"person lifting weights","🏋️‍♀️":"woman lifting weights","🏋️‍♂️":"man lifting weights","🏌":"person golfing","🏌️‍♀️":"woman golfing","🏌️‍♂️":"man golfing","👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","👨‍❤️‍👨":"couple with heart: man, man","👨‍❤️‍💋‍👨":"kiss: man, man","👨‍👦":"family: man, boy","👨‍👦‍👦":"family: man, boy, boy","👨‍👧":"family: man, girl","👨‍👧‍👦":"family: man, girl, boy","👨‍👧‍👧":"family: man, girl, girl","👨‍👨‍👦":"family: man, man, boy","👨‍👨‍👦‍👦":"family: man, man, boy, boy","👨‍👨‍👧":"family: man, man, girl","👨‍👨‍👧‍👦":"family: man, man, girl, boy","👨‍👨‍👧‍👧":"family: man, man, girl, girl","👨‍👩‍👦":"family: man, woman, boy","👨‍👩‍👦‍👦":"family: man, woman, boy, boy","👨‍👩‍👧":"family: man, woman, girl","👨‍👩‍👧‍👦":"family: man, woman, girl, boy","👨‍👩‍👧‍👧":"family: man, woman, girl, girl","👩‍❤️‍👨":"couple with heart: woman, man","👩‍❤️‍👩":"couple with heart: woman, woman","👩‍❤️‍💋‍👨":"kiss: woman, man","👩‍❤️‍💋‍👩":"kiss: woman, woman","👩‍👦":"family: woman, boy","👩‍👦‍👦":"family: woman, boy, boy","👩‍👧":"family: woman, girl","👩‍👧‍👦":"family: woman, girl, boy","👩‍👧‍👧":"family: woman, girl, girl","👩‍👩‍👦":"family: woman, woman, boy","👩‍👩‍👦‍👦":"family: woman, woman, boy, boy","👩‍👩‍👧":"family: woman, woman, girl","👩‍👩‍👧‍👦":"family: woman, woman, girl, boy","👩‍👩‍👧‍👧":"family: woman, woman, girl, girl","👪":"family","👫":"woman and man holding hands","👬":"men holding hands","👭":"women holding hands","💏":"kiss","💑":"couple with heart","🗣":"speaking head","🚣":"person rowing boat","🚣‍♀️":"woman rowing boat","🚣‍♂️":"man rowing boat","🚴":"person biking","🚴‍♀️":"woman biking","🚴‍♂️":"man biking","🚵":"person mountain biking","🚵‍♀️":"woman mountain biking","🚵‍♂️":"man mountain biking","🛀":"person taking bath","🛌":"person in bed","🤸":"person cartwheeling","🤸‍♀️":"woman cartwheeling","🤸‍♂️":"man cartwheeling","🤹":"person juggling","🤹‍♀️":"woman juggling","🤹‍♂️":"man juggling","🤺":"person fencing","🤼":"people wrestling","🤼‍♀️":"women wrestling","🤼‍♂️":"men wrestling","🤽":"person playing water polo","🤽‍♀️":"woman playing water polo","🤽‍♂️":"man playing water polo","🤾":"person playing handball","🤾‍♀️":"woman playing handball","🤾‍♂️":"man playing handball","🧑‍🤝‍🧑":"people holding hands","🧘":"person in lotus position","🧘‍♀️":"woman in lotus position","🧘‍♂️":"man in lotus position"},"person-symbol":{"👣":"footprints","👤":"bust in silhouette","👥":"busts in silhouette","🗣":"speaking head"}},"Smileys & Emotion":{"cat-face":{"❣":"heart exclamation","❤":"red heart","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🧡":"orange heart"},emotion:{"❣":"heart exclamation","❤":"red heart","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","🤍":"white heart","🤎":"brown heart","🧡":"orange heart"},"face-affection":{"☠":"skull and crossbones","☹":"frowning face","☺":"smiling face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😋":"face savoring food","😌":"relieved face","😍":"smiling face with heart-eyes","😎":"smiling face with sunglasses","😏":"smirking face","😐":"neutral face","😑":"expressionless face","😒":"unamused face","😓":"downcast face with sweat","😔":"pensive face","😕":"confused face","😖":"confounded face","😗":"kissing face","😘":"face blowing a kiss","😙":"kissing face with smiling eyes","😚":"kissing face with closed eyes","😛":"face with tongue","😜":"winking face with tongue","😝":"squinting face with tongue","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😪":"sleepy face","😫":"tired face","😬":"grimacing face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😴":"sleeping face","😵":"dizzy face","😶":"face without mouth","😷":"face with medical mask","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙄":"face with rolling eyes","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤐":"zipper-mouth face","🤑":"money-mouth face","🤒":"face with thermometer","🤓":"nerd face","🤔":"thinking face","🤕":"face with head-bandage","🤖":"robot","🤗":"hugging face","🤠":"cowboy hat face","🤡":"clown face","🤢":"nauseated face","🤤":"drooling face","🤥":"lying face","🤧":"sneezing face","🤨":"face with raised eyebrow","🤩":"star-struck","🤪":"zany face","🤫":"shushing face","🤬":"face with symbols on mouth","🤭":"face with hand over mouth","🤮":"face vomiting","🤯":"exploding head","🥰":"smiling face with hearts","🥱":"yawning face","🥳":"partying face","🥴":"woozy face","🥵":"hot face","🥶":"cold face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"face-concerned":{"☠":"skull and crossbones","☹":"frowning face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😓":"downcast face with sweat","😕":"confused face","😖":"confounded face","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😫":"tired face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤖":"robot","🤡":"clown face","🤬":"face with symbols on mouth","🥱":"yawning face","🥺":"pleading face","🧡":"orange heart"},"face-costume":{"❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤖":"robot","🤡":"clown face","🧡":"orange heart"},"face-glasses":{"☠":"skull and crossbones","☹":"frowning face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😎":"smiling face with sunglasses","😓":"downcast face with sweat","😕":"confused face","😖":"confounded face","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😫":"tired face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤓":"nerd face","🤖":"robot","🤡":"clown face","🤬":"face with symbols on mouth","🥱":"yawning face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"face-hand":{"☠":"skull and crossbones","☹":"frowning face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😌":"relieved face","😎":"smiling face with sunglasses","😏":"smirking face","😐":"neutral face","😑":"expressionless face","😒":"unamused face","😓":"downcast face with sweat","😔":"pensive face","😕":"confused face","😖":"confounded face","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😪":"sleepy face","😫":"tired face","😬":"grimacing face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😴":"sleeping face","😵":"dizzy face","😶":"face without mouth","😷":"face with medical mask","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙄":"face with rolling eyes","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤐":"zipper-mouth face","🤒":"face with thermometer","🤓":"nerd face","🤔":"thinking face","🤕":"face with head-bandage","🤖":"robot","🤗":"hugging face","🤠":"cowboy hat face","🤡":"clown face","🤢":"nauseated face","🤤":"drooling face","🤥":"lying face","🤧":"sneezing face","🤨":"face with raised eyebrow","🤫":"shushing face","🤬":"face with symbols on mouth","🤭":"face with hand over mouth","🤮":"face vomiting","🤯":"exploding head","🥱":"yawning face","🥳":"partying face","🥴":"woozy face","🥵":"hot face","🥶":"cold face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"face-hat":{"☠":"skull and crossbones","☹":"frowning face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😎":"smiling face with sunglasses","😓":"downcast face with sweat","😕":"confused face","😖":"confounded face","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😫":"tired face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤓":"nerd face","🤖":"robot","🤠":"cowboy hat face","🤡":"clown face","🤬":"face with symbols on mouth","🥱":"yawning face","🥳":"partying face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"face-negative":{"☠":"skull and crossbones","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😠":"angry face","😡":"pouting face","😤":"face with steam from nose","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤖":"robot","🤡":"clown face","🤬":"face with symbols on mouth","🧡":"orange heart"},"face-neutral-skeptical":{"☠":"skull and crossbones","☹":"frowning face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😌":"relieved face","😎":"smiling face with sunglasses","😏":"smirking face","😐":"neutral face","😑":"expressionless face","😒":"unamused face","😓":"downcast face with sweat","😔":"pensive face","😕":"confused face","😖":"confounded face","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😪":"sleepy face","😫":"tired face","😬":"grimacing face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😴":"sleeping face","😵":"dizzy face","😶":"face without mouth","😷":"face with medical mask","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙄":"face with rolling eyes","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤐":"zipper-mouth face","🤒":"face with thermometer","🤓":"nerd face","🤕":"face with head-bandage","🤖":"robot","🤠":"cowboy hat face","🤡":"clown face","🤢":"nauseated face","🤤":"drooling face","🤥":"lying face","🤧":"sneezing face","🤨":"face with raised eyebrow","🤬":"face with symbols on mouth","🤮":"face vomiting","🤯":"exploding head","🥱":"yawning face","🥳":"partying face","🥴":"woozy face","🥵":"hot face","🥶":"cold face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"face-sleepy":{"☠":"skull and crossbones","☹":"frowning face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😌":"relieved face","😎":"smiling face with sunglasses","😓":"downcast face with sweat","😔":"pensive face","😕":"confused face","😖":"confounded face","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😪":"sleepy face","😫":"tired face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😴":"sleeping face","😵":"dizzy face","😷":"face with medical mask","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤒":"face with thermometer","🤓":"nerd face","🤕":"face with head-bandage","🤖":"robot","🤠":"cowboy hat face","🤡":"clown face","🤢":"nauseated face","🤤":"drooling face","🤧":"sneezing face","🤬":"face with symbols on mouth","🤮":"face vomiting","🤯":"exploding head","🥱":"yawning face","🥳":"partying face","🥴":"woozy face","🥵":"hot face","🥶":"cold face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"face-smiling":{"☠":"skull and crossbones","☹":"frowning face","☺":"smiling face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😀":"grinning face","😁":"beaming face with smiling eyes","😂":"face with tears of joy","😃":"grinning face with big eyes","😄":"grinning face with smiling eyes","😅":"grinning face with sweat","😆":"grinning squinting face","😇":"smiling face with halo","😈":"smiling face with horns","😉":"winking face","😊":"smiling face with smiling eyes","😋":"face savoring food","😌":"relieved face","😍":"smiling face with heart-eyes","😎":"smiling face with sunglasses","😏":"smirking face","😐":"neutral face","😑":"expressionless face","😒":"unamused face","😓":"downcast face with sweat","😔":"pensive face","😕":"confused face","😖":"confounded face","😗":"kissing face","😘":"face blowing a kiss","😙":"kissing face with smiling eyes","😚":"kissing face with closed eyes","😛":"face with tongue","😜":"winking face with tongue","😝":"squinting face with tongue","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😪":"sleepy face","😫":"tired face","😬":"grimacing face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😴":"sleeping face","😵":"dizzy face","😶":"face without mouth","😷":"face with medical mask","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙂":"slightly smiling face","🙃":"upside-down face","🙄":"face with rolling eyes","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤐":"zipper-mouth face","🤑":"money-mouth face","🤒":"face with thermometer","🤓":"nerd face","🤔":"thinking face","🤕":"face with head-bandage","🤖":"robot","🤗":"hugging face","🤠":"cowboy hat face","🤡":"clown face","🤢":"nauseated face","🤣":"rolling on the floor laughing","🤤":"drooling face","🤥":"lying face","🤧":"sneezing face","🤨":"face with raised eyebrow","🤩":"star-struck","🤪":"zany face","🤫":"shushing face","🤬":"face with symbols on mouth","🤭":"face with hand over mouth","🤮":"face vomiting","🤯":"exploding head","🥰":"smiling face with hearts","🥱":"yawning face","🥳":"partying face","🥴":"woozy face","🥵":"hot face","🥶":"cold face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"face-tongue":{"☠":"skull and crossbones","☹":"frowning face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😋":"face savoring food","😌":"relieved face","😎":"smiling face with sunglasses","😏":"smirking face","😐":"neutral face","😑":"expressionless face","😒":"unamused face","😓":"downcast face with sweat","😔":"pensive face","😕":"confused face","😖":"confounded face","😛":"face with tongue","😜":"winking face with tongue","😝":"squinting face with tongue","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😪":"sleepy face","😫":"tired face","😬":"grimacing face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😴":"sleeping face","😵":"dizzy face","😶":"face without mouth","😷":"face with medical mask","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙄":"face with rolling eyes","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤐":"zipper-mouth face","🤑":"money-mouth face","🤒":"face with thermometer","🤓":"nerd face","🤔":"thinking face","🤕":"face with head-bandage","🤖":"robot","🤗":"hugging face","🤠":"cowboy hat face","🤡":"clown face","🤢":"nauseated face","🤤":"drooling face","🤥":"lying face","🤧":"sneezing face","🤨":"face with raised eyebrow","🤪":"zany face","🤫":"shushing face","🤬":"face with symbols on mouth","🤭":"face with hand over mouth","🤮":"face vomiting","🤯":"exploding head","🥱":"yawning face","🥳":"partying face","🥴":"woozy face","🥵":"hot face","🥶":"cold face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"face-unwell":{"☠":"skull and crossbones","☹":"frowning face","❣":"heart exclamation","❤":"red heart","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien monster","👿":"angry face with horns","💀":"skull","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💩":"pile of poo","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","😈":"smiling face with horns","😎":"smiling face with sunglasses","😓":"downcast face with sweat","😕":"confused face","😖":"confounded face","😞":"disappointed face","😟":"worried face","😠":"angry face","😡":"pouting face","😢":"crying face","😣":"persevering face","😤":"face with steam from nose","😥":"sad but relieved face","😦":"frowning face with open mouth","😧":"anguished face","😨":"fearful face","😩":"weary face","😫":"tired face","😭":"loudly crying face","😮":"face with open mouth","😯":"hushed face","😰":"anxious face with sweat","😱":"face screaming in fear","😲":"astonished face","😳":"flushed face","😵":"dizzy face","😷":"face with medical mask","😸":"grinning cat with smiling eyes","😹":"cat with tears of joy","😺":"grinning cat","😻":"smiling cat with heart-eyes","😼":"cat with wry smile","😽":"kissing cat","😾":"pouting cat","😿":"crying cat","🙀":"weary cat","🙁":"slightly frowning face","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🤒":"face with thermometer","🤓":"nerd face","🤕":"face with head-bandage","🤖":"robot","🤠":"cowboy hat face","🤡":"clown face","🤢":"nauseated face","🤧":"sneezing face","🤬":"face with symbols on mouth","🤮":"face vomiting","🤯":"exploding head","🥱":"yawning face","🥳":"partying face","🥴":"woozy face","🥵":"hot face","🥶":"cold face","🥺":"pleading face","🧐":"face with monocle","🧡":"orange heart"},"monkey-face":{"❣":"heart exclamation","❤":"red heart","💋":"kiss mark","💌":"love letter","💓":"beating heart","💔":"broken heart","💕":"two hearts","💖":"sparkling heart","💗":"growing heart","💘":"heart with arrow","💙":"blue heart","💚":"green heart","💛":"yellow heart","💜":"purple heart","💝":"heart with ribbon","💞":"revolving hearts","💟":"heart decoration","💢":"anger symbol","💣":"bomb","💤":"zzz","💥":"collision","💦":"sweat droplets","💨":"dashing away","💫":"dizzy","💬":"speech balloon","💭":"thought balloon","💯":"hundred points","🕳":"hole","🖤":"black heart","🗨":"left speech bubble","🗯":"right anger bubble","🙈":"see-no-evil monkey","🙉":"hear-no-evil monkey","🙊":"speak-no-evil monkey","🤍":"white heart","🤎":"brown heart","🧡":"orange heart"}},Symbols:{alphanum:{"ℹ":"information","Ⓜ":"circled M","▪":"black small square","▫":"white small square","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","⚪":"white circle","⚫":"black circle","⬛":"black large square","⬜":"white large square","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","💠":"diamond with a dot","🔘":"radio button","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},arrow:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","↔":"left-right arrow","↕":"up-down arrow","↖":"up-left arrow","↗":"up-right arrow","↘":"down-right arrow","↙":"down-left arrow","↩":"right arrow curving left","↪":"left arrow curving right","⏏":"eject button","⏩":"fast-forward button","⏪":"fast reverse button","⏫":"fast up button","⏬":"fast down button","⏭":"next track button","⏮":"last track button","⏯":"play or pause button","⏸":"pause button","⏹":"stop button","⏺":"record button","Ⓜ":"circled M","▪":"black small square","▫":"white small square","▶":"play button","◀":"reverse button","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","☦":"orthodox cross","☪":"star and crescent","☮":"peace symbol","☯":"yin yang","☸":"wheel of dharma","♀":"female sign","♂":"male sign","♈":"Aries","♉":"Taurus","♊":"Gemini","♋":"Cancer","♌":"Leo","♍":"Virgo","♎":"Libra","♏":"Scorpio","♐":"Sagittarius","♑":"Capricorn","♒":"Aquarius","♓":"Pisces","♻":"recycling symbol","♾":"infinity","⚕":"medical symbol","⚛":"atom symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","⛎":"Ophiuchus","✅":"check mark button","✔":"check mark","✖":"multiply","✝":"latin cross","✡":"star of David","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➕":"plus","➖":"minus","➗":"divide","➡":"right arrow","➰":"curly loop","➿":"double curly loop","⤴":"right arrow curving up","⤵":"right arrow curving down","⬅":"left arrow","⬆":"up arrow","⬇":"down arrow","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","🎦":"cinema","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","📳":"vibration mode","📴":"mobile phone off","📶":"antenna bars","🔀":"shuffle tracks button","🔁":"repeat button","🔂":"repeat single button","🔃":"clockwise vertical arrows","🔄":"counterclockwise arrows button","🔅":"dim button","🔆":"bright button","🔘":"radio button","🔙":"BACK arrow","🔚":"END arrow","🔛":"ON! arrow","🔜":"SOON arrow","🔝":"TOP arrow","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔯":"dotted six-pointed star","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🔼":"upwards button","🔽":"downwards button","🕉":"om","🕎":"menorah","🛐":"place of worship","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},"av-symbol":{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","⏏":"eject button","⏩":"fast-forward button","⏪":"fast reverse button","⏫":"fast up button","⏬":"fast down button","⏭":"next track button","⏮":"last track button","⏯":"play or pause button","⏸":"pause button","⏹":"stop button","⏺":"record button","Ⓜ":"circled M","▪":"black small square","▫":"white small square","▶":"play button","◀":"reverse button","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","♀":"female sign","♂":"male sign","♻":"recycling symbol","♾":"infinity","⚕":"medical symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","✅":"check mark button","✔":"check mark","✖":"multiply","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➕":"plus","➖":"minus","➗":"divide","➰":"curly loop","➿":"double curly loop","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","🎦":"cinema","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","📳":"vibration mode","📴":"mobile phone off","📶":"antenna bars","🔀":"shuffle tracks button","🔁":"repeat button","🔂":"repeat single button","🔅":"dim button","🔆":"bright button","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🔼":"upwards button","🔽":"downwards button","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},currency:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","™":"trade mark","ℹ":"information","Ⓜ":"circled M","▪":"black small square","▫":"white small square","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","♻":"recycling symbol","⚕":"medical symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","✅":"check mark button","✔":"check mark","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","➰":"curly loop","➿":"double curly loop","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},gender:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","Ⓜ":"circled M","▪":"black small square","▫":"white small square","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","♀":"female sign","♂":"male sign","♻":"recycling symbol","♾":"infinity","⚕":"medical symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","✅":"check mark button","✔":"check mark","✖":"multiply","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➕":"plus","➖":"minus","➗":"divide","➰":"curly loop","➿":"double curly loop","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},geometric:{"▪":"black small square","▫":"white small square","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","⚪":"white circle","⚫":"black circle","⬛":"black large square","⬜":"white large square","💠":"diamond with a dot","🔘":"radio button","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},keycap:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","ℹ":"information","Ⓜ":"circled M","▪":"black small square","▫":"white small square","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","⚪":"white circle","⚫":"black circle","⬛":"black large square","⬜":"white large square","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","💠":"diamond with a dot","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},math:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","Ⓜ":"circled M","▪":"black small square","▫":"white small square","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","♻":"recycling symbol","♾":"infinity","⚕":"medical symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","✅":"check mark button","✔":"check mark","✖":"multiply","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➕":"plus","➖":"minus","➗":"divide","➰":"curly loop","➿":"double curly loop","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},"other-symbol":{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","™":"trade mark","ℹ":"information","Ⓜ":"circled M","▪":"black small square","▫":"white small square","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","♻":"recycling symbol","⚕":"medical symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","✅":"check mark button","✔":"check mark","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","➰":"curly loop","➿":"double curly loop","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","💠":"diamond with a dot","📛":"name badge","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},punctuation:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","Ⓜ":"circled M","▪":"black small square","▫":"white small square","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","♻":"recycling symbol","⚕":"medical symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","✅":"check mark button","✔":"check mark","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➰":"curly loop","➿":"double curly loop","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},religion:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","⏏":"eject button","⏩":"fast-forward button","⏪":"fast reverse button","⏫":"fast up button","⏬":"fast down button","⏭":"next track button","⏮":"last track button","⏯":"play or pause button","⏸":"pause button","⏹":"stop button","⏺":"record button","Ⓜ":"circled M","▪":"black small square","▫":"white small square","▶":"play button","◀":"reverse button","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","☦":"orthodox cross","☪":"star and crescent","☮":"peace symbol","☯":"yin yang","☸":"wheel of dharma","♀":"female sign","♂":"male sign","♈":"Aries","♉":"Taurus","♊":"Gemini","♋":"Cancer","♌":"Leo","♍":"Virgo","♎":"Libra","♏":"Scorpio","♐":"Sagittarius","♑":"Capricorn","♒":"Aquarius","♓":"Pisces","♻":"recycling symbol","♾":"infinity","⚕":"medical symbol","⚛":"atom symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","⛎":"Ophiuchus","✅":"check mark button","✔":"check mark","✖":"multiply","✝":"latin cross","✡":"star of David","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➕":"plus","➖":"minus","➗":"divide","➰":"curly loop","➿":"double curly loop","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","🎦":"cinema","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","📳":"vibration mode","📴":"mobile phone off","📶":"antenna bars","🔀":"shuffle tracks button","🔁":"repeat button","🔂":"repeat single button","🔅":"dim button","🔆":"bright button","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔯":"dotted six-pointed star","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🔼":"upwards button","🔽":"downwards button","🕉":"om","🕎":"menorah","🛐":"place of worship","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},"transport-sign":{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","↔":"left-right arrow","↕":"up-down arrow","↖":"up-left arrow","↗":"up-right arrow","↘":"down-right arrow","↙":"down-left arrow","↩":"right arrow curving left","↪":"left arrow curving right","⏏":"eject button","⏩":"fast-forward button","⏪":"fast reverse button","⏫":"fast up button","⏬":"fast down button","⏭":"next track button","⏮":"last track button","⏯":"play or pause button","⏸":"pause button","⏹":"stop button","⏺":"record button","Ⓜ":"circled M","▪":"black small square","▫":"white small square","▶":"play button","◀":"reverse button","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","☢":"radioactive","☣":"biohazard","☦":"orthodox cross","☪":"star and crescent","☮":"peace symbol","☯":"yin yang","☸":"wheel of dharma","♀":"female sign","♂":"male sign","♈":"Aries","♉":"Taurus","♊":"Gemini","♋":"Cancer","♌":"Leo","♍":"Virgo","♎":"Libra","♏":"Scorpio","♐":"Sagittarius","♑":"Capricorn","♒":"Aquarius","♓":"Pisces","♻":"recycling symbol","♾":"infinity","♿":"wheelchair symbol","⚕":"medical symbol","⚛":"atom symbol","⚜":"fleur-de-lis","⚠":"warning","⚪":"white circle","⚫":"black circle","⛎":"Ophiuchus","⛔":"no entry","✅":"check mark button","✔":"check mark","✖":"multiply","✝":"latin cross","✡":"star of David","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➕":"plus","➖":"minus","➗":"divide","➡":"right arrow","➰":"curly loop","➿":"double curly loop","⤴":"right arrow curving up","⤵":"right arrow curving down","⬅":"left arrow","⬆":"up arrow","⬇":"down arrow","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","🎦":"cinema","🏧":"ATM sign","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","📳":"vibration mode","📴":"mobile phone off","📵":"no mobile phones","📶":"antenna bars","🔀":"shuffle tracks button","🔁":"repeat button","🔂":"repeat single button","🔃":"clockwise vertical arrows","🔄":"counterclockwise arrows button","🔅":"dim button","🔆":"bright button","🔘":"radio button","🔙":"BACK arrow","🔚":"END arrow","🔛":"ON! arrow","🔜":"SOON arrow","🔝":"TOP arrow","🔞":"no one under eighteen","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔯":"dotted six-pointed star","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🔼":"upwards button","🔽":"downwards button","🕉":"om","🕎":"menorah","🚫":"prohibited","🚭":"no smoking","🚮":"litter in bin sign","🚯":"no littering","🚰":"potable water","🚱":"non-potable water","🚳":"no bicycles","🚷":"no pedestrians","🚸":"children crossing","🚹":"men’s room","🚺":"women’s room","🚻":"restroom","🚼":"baby symbol","🚾":"water closet","🛂":"passport control","🛃":"customs","🛄":"baggage claim","🛅":"left luggage","🛐":"place of worship","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},warning:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","↔":"left-right arrow","↕":"up-down arrow","↖":"up-left arrow","↗":"up-right arrow","↘":"down-right arrow","↙":"down-left arrow","↩":"right arrow curving left","↪":"left arrow curving right","⏏":"eject button","⏩":"fast-forward button","⏪":"fast reverse button","⏫":"fast up button","⏬":"fast down button","⏭":"next track button","⏮":"last track button","⏯":"play or pause button","⏸":"pause button","⏹":"stop button","⏺":"record button","Ⓜ":"circled M","▪":"black small square","▫":"white small square","▶":"play button","◀":"reverse button","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","☢":"radioactive","☣":"biohazard","☦":"orthodox cross","☪":"star and crescent","☮":"peace symbol","☯":"yin yang","☸":"wheel of dharma","♀":"female sign","♂":"male sign","♈":"Aries","♉":"Taurus","♊":"Gemini","♋":"Cancer","♌":"Leo","♍":"Virgo","♎":"Libra","♏":"Scorpio","♐":"Sagittarius","♑":"Capricorn","♒":"Aquarius","♓":"Pisces","♻":"recycling symbol","♾":"infinity","⚕":"medical symbol","⚛":"atom symbol","⚜":"fleur-de-lis","⚠":"warning","⚪":"white circle","⚫":"black circle","⛎":"Ophiuchus","⛔":"no entry","✅":"check mark button","✔":"check mark","✖":"multiply","✝":"latin cross","✡":"star of David","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➕":"plus","➖":"minus","➗":"divide","➡":"right arrow","➰":"curly loop","➿":"double curly loop","⤴":"right arrow curving up","⤵":"right arrow curving down","⬅":"left arrow","⬆":"up arrow","⬇":"down arrow","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","🎦":"cinema","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","📳":"vibration mode","📴":"mobile phone off","📵":"no mobile phones","📶":"antenna bars","🔀":"shuffle tracks button","🔁":"repeat button","🔂":"repeat single button","🔃":"clockwise vertical arrows","🔄":"counterclockwise arrows button","🔅":"dim button","🔆":"bright button","🔘":"radio button","🔙":"BACK arrow","🔚":"END arrow","🔛":"ON! arrow","🔜":"SOON arrow","🔝":"TOP arrow","🔞":"no one under eighteen","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔯":"dotted six-pointed star","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🔼":"upwards button","🔽":"downwards button","🕉":"om","🕎":"menorah","🚫":"prohibited","🚭":"no smoking","🚯":"no littering","🚱":"non-potable water","🚳":"no bicycles","🚷":"no pedestrians","🚸":"children crossing","🛐":"place of worship","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"},zodiac:{"#️⃣":"keycap: #","*️⃣":"keycap: *","0️⃣":"keycap: 0","1️⃣":"keycap: 1","2️⃣":"keycap: 2","3️⃣":"keycap: 3","4️⃣":"keycap: 4","5️⃣":"keycap: 5","6️⃣":"keycap: 6","7️⃣":"keycap: 7","8️⃣":"keycap: 8","9️⃣":"keycap: 9","©":"copyright","®":"registered","‼":"double exclamation mark","⁉":"exclamation question mark","™":"trade mark","ℹ":"information","⏏":"eject button","⏩":"fast-forward button","⏪":"fast reverse button","⏫":"fast up button","⏬":"fast down button","⏭":"next track button","⏮":"last track button","⏯":"play or pause button","⏸":"pause button","⏹":"stop button","⏺":"record button","Ⓜ":"circled M","▪":"black small square","▫":"white small square","▶":"play button","◀":"reverse button","◻":"white medium square","◼":"black medium square","◽":"white medium-small square","◾":"black medium-small square","☑":"check box with check","♀":"female sign","♂":"male sign","♈":"Aries","♉":"Taurus","♊":"Gemini","♋":"Cancer","♌":"Leo","♍":"Virgo","♎":"Libra","♏":"Scorpio","♐":"Sagittarius","♑":"Capricorn","♒":"Aquarius","♓":"Pisces","♻":"recycling symbol","♾":"infinity","⚕":"medical symbol","⚜":"fleur-de-lis","⚪":"white circle","⚫":"black circle","⛎":"Ophiuchus","✅":"check mark button","✔":"check mark","✖":"multiply","✳":"eight-spoked asterisk","✴":"eight-pointed star","❇":"sparkle","❌":"cross mark","❎":"cross mark button","❓":"question mark","❔":"white question mark","❕":"white exclamation mark","❗":"exclamation mark","➕":"plus","➖":"minus","➗":"divide","➰":"curly loop","➿":"double curly loop","⬛":"black large square","⬜":"white large square","⭕":"hollow red circle","〰":"wavy dash","〽":"part alternation mark","㊗":"Japanese “congratulations” button","㊙":"Japanese “secret” button","🅰":"A button (blood type)","🅱":"B button (blood type)","🅾":"O button (blood type)","🅿":"P button","🆎":"AB button (blood type)","🆑":"CL button","🆒":"COOL button","🆓":"FREE button","🆔":"ID button","🆕":"NEW button","🆖":"NG button","🆗":"OK button","🆘":"SOS button","🆙":"UP! button","🆚":"VS button","🈁":"Japanese “here” button","🈂":"Japanese “service charge” button","🈚":"Japanese “free of charge” button","🈯":"Japanese “reserved” button","🈲":"Japanese “prohibited” button","🈳":"Japanese “vacancy” button","🈴":"Japanese “passing grade” button","🈵":"Japanese “no vacancy” button","🈶":"Japanese “not free of charge” button","🈷":"Japanese “monthly amount” button","🈸":"Japanese “application” button","🈹":"Japanese “discount” button","🈺":"Japanese “open for business” button","🉐":"Japanese “bargain” button","🉑":"Japanese “acceptable” button","🎦":"cinema","💠":"diamond with a dot","💱":"currency exchange","💲":"heavy dollar sign","📛":"name badge","📳":"vibration mode","📴":"mobile phone off","📶":"antenna bars","🔀":"shuffle tracks button","🔁":"repeat button","🔂":"repeat single button","🔅":"dim button","🔆":"bright button","🔘":"radio button","🔟":"keycap: 10","🔠":"input latin uppercase","🔡":"input latin lowercase","🔢":"input numbers","🔣":"input symbols","🔤":"input latin letters","🔰":"Japanese symbol for beginner","🔱":"trident emblem","🔲":"black square button","🔳":"white square button","🔴":"red circle","🔵":"blue circle","🔶":"large orange diamond","🔷":"large blue diamond","🔸":"small orange diamond","🔹":"small blue diamond","🔺":"red triangle pointed up","🔻":"red triangle pointed down","🔼":"upwards button","🔽":"downwards button","🟠":"orange circle","🟡":"yellow circle","🟢":"green circle","🟣":"purple circle","🟤":"brown circle","🟥":"red square","🟦":"blue square","🟧":"orange square","🟨":"yellow square","🟩":"green square","🟪":"purple square","🟫":"brown square"}},"Travel & Places":{hotel:{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛱":"umbrella on ground","❄":"snowflake","⭐":"star","🌀":"cyclone","🌂":"closed umbrella","🌈":"rainbow","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","💧":"droplet","🔥":"fire","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🛎":"bellhop bell","🧳":"luggage","🪐":"ringed planet"},"place-building":{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","♨":"hot springs","⚓":"anchor","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛩":"shinto shrine","⛪":"church","⛱":"umbrella on ground","⛲":"fountain","⛴":"ferry","⛵":"sailboat","⛺":"tent","⛽":"fuel pump","✈":"airplane","❄":"snowflake","⭐":"star","🌀":"cyclone","🌁":"foggy","🌂":"closed umbrella","🌃":"night with stars","🌄":"sunrise over mountains","🌅":"sunrise","🌆":"cityscape at dusk","🌇":"sunset","🌈":"rainbow","🌉":"bridge at night","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","🎠":"carousel horse","🎡":"ferris wheel","🎢":"roller coaster","🎪":"circus tent","🏍":"motorcycle","🏎":"racing car","🏗":"building construction","🏘":"houses","🏙":"cityscape","🏚":"derelict house","🏛":"classical building","🏟":"stadium","🏠":"house","🏡":"house with garden","🏢":"office building","🏣":"Japanese post office","🏤":"post office","🏥":"hospital","🏦":"bank","🏨":"hotel","🏩":"love hotel","🏪":"convenience store","🏫":"school","🏬":"department store","🏭":"factory","🏯":"Japanese castle","🏰":"castle","💈":"barber pole","💒":"wedding","💧":"droplet","💺":"seat","🔥":"fire","🕋":"kaaba","🕌":"mosque","🕍":"synagogue","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🗼":"Tokyo tower","🗽":"Statue of Liberty","🚀":"rocket","🚁":"helicopter","🚂":"locomotive","🚃":"railway car","🚄":"high-speed train","🚅":"bullet train","🚆":"train","🚇":"metro","🚈":"light rail","🚉":"station","🚊":"tram","🚋":"tram car","🚌":"bus","🚍":"oncoming bus","🚎":"trolleybus","🚏":"bus stop","🚐":"minibus","🚑":"ambulance","🚒":"fire engine","🚓":"police car","🚔":"oncoming police car","🚕":"taxi","🚖":"oncoming taxi","🚗":"automobile","🚘":"oncoming automobile","🚙":"sport utility vehicle","🚚":"delivery truck","🚛":"articulated lorry","🚜":"tractor","🚝":"monorail","🚞":"mountain railway","🚟":"suspension railway","🚠":"mountain cableway","🚡":"aerial tramway","🚢":"ship","🚤":"speedboat","🚥":"horizontal traffic light","🚦":"vertical traffic light","🚧":"construction","🚨":"police car light","🚲":"bicycle","🛎":"bellhop bell","🛑":"stop sign","🛕":"hindu temple","🛢":"oil drum","🛣":"motorway","🛤":"railway track","🛥":"motor boat","🛩":"small airplane","🛫":"airplane departure","🛬":"airplane arrival","🛰":"satellite","🛳":"passenger ship","🛴":"kick scooter","🛵":"motor scooter","🛶":"canoe","🛸":"flying saucer","🛹":"skateboard","🛺":"auto rickshaw","🦼":"motorized wheelchair","🦽":"manual wheelchair","🧱":"brick","🧳":"luggage","🪂":"parachute","🪐":"ringed planet"},"place-geographic":{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","♨":"hot springs","⚓":"anchor","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛩":"shinto shrine","⛪":"church","⛰":"mountain","⛱":"umbrella on ground","⛲":"fountain","⛴":"ferry","⛵":"sailboat","⛺":"tent","⛽":"fuel pump","✈":"airplane","❄":"snowflake","⭐":"star","🌀":"cyclone","🌁":"foggy","🌂":"closed umbrella","🌃":"night with stars","🌄":"sunrise over mountains","🌅":"sunrise","🌆":"cityscape at dusk","🌇":"sunset","🌈":"rainbow","🌉":"bridge at night","🌊":"water wave","🌋":"volcano","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","🎠":"carousel horse","🎡":"ferris wheel","🎢":"roller coaster","🎪":"circus tent","🏍":"motorcycle","🏎":"racing car","🏔":"snow-capped mountain","🏕":"camping","🏖":"beach with umbrella","🏗":"building construction","🏘":"houses","🏙":"cityscape","🏚":"derelict house","🏛":"classical building","🏜":"desert","🏝":"desert island","🏞":"national park","🏟":"stadium","🏠":"house","🏡":"house with garden","🏢":"office building","🏣":"Japanese post office","🏤":"post office","🏥":"hospital","🏦":"bank","🏨":"hotel","🏩":"love hotel","🏪":"convenience store","🏫":"school","🏬":"department store","🏭":"factory","🏯":"Japanese castle","🏰":"castle","💈":"barber pole","💒":"wedding","💧":"droplet","💺":"seat","🔥":"fire","🕋":"kaaba","🕌":"mosque","🕍":"synagogue","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🗻":"mount fuji","🗼":"Tokyo tower","🗽":"Statue of Liberty","🚀":"rocket","🚁":"helicopter","🚂":"locomotive","🚃":"railway car","🚄":"high-speed train","🚅":"bullet train","🚆":"train","🚇":"metro","🚈":"light rail","🚉":"station","🚊":"tram","🚋":"tram car","🚌":"bus","🚍":"oncoming bus","🚎":"trolleybus","🚏":"bus stop","🚐":"minibus","🚑":"ambulance","🚒":"fire engine","🚓":"police car","🚔":"oncoming police car","🚕":"taxi","🚖":"oncoming taxi","🚗":"automobile","🚘":"oncoming automobile","🚙":"sport utility vehicle","🚚":"delivery truck","🚛":"articulated lorry","🚜":"tractor","🚝":"monorail","🚞":"mountain railway","🚟":"suspension railway","🚠":"mountain cableway","🚡":"aerial tramway","🚢":"ship","🚤":"speedboat","🚥":"horizontal traffic light","🚦":"vertical traffic light","🚧":"construction","🚨":"police car light","🚲":"bicycle","🛎":"bellhop bell","🛑":"stop sign","🛕":"hindu temple","🛢":"oil drum","🛣":"motorway","🛤":"railway track","🛥":"motor boat","🛩":"small airplane","🛫":"airplane departure","🛬":"airplane arrival","🛰":"satellite","🛳":"passenger ship","🛴":"kick scooter","🛵":"motor scooter","🛶":"canoe","🛸":"flying saucer","🛹":"skateboard","🛺":"auto rickshaw","🦼":"motorized wheelchair","🦽":"manual wheelchair","🧱":"brick","🧳":"luggage","🪂":"parachute","🪐":"ringed planet"},"place-map":{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","♨":"hot springs","⚓":"anchor","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛩":"shinto shrine","⛪":"church","⛰":"mountain","⛱":"umbrella on ground","⛲":"fountain","⛴":"ferry","⛵":"sailboat","⛺":"tent","⛽":"fuel pump","✈":"airplane","❄":"snowflake","⭐":"star","🌀":"cyclone","🌁":"foggy","🌂":"closed umbrella","🌃":"night with stars","🌄":"sunrise over mountains","🌅":"sunrise","🌆":"cityscape at dusk","🌇":"sunset","🌈":"rainbow","🌉":"bridge at night","🌊":"water wave","🌋":"volcano","🌌":"milky way","🌍":"globe showing Europe-Africa","🌎":"globe showing Americas","🌏":"globe showing Asia-Australia","🌐":"globe with meridians","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","🎠":"carousel horse","🎡":"ferris wheel","🎢":"roller coaster","🎪":"circus tent","🏍":"motorcycle","🏎":"racing car","🏔":"snow-capped mountain","🏕":"camping","🏖":"beach with umbrella","🏗":"building construction","🏘":"houses","🏙":"cityscape","🏚":"derelict house","🏛":"classical building","🏜":"desert","🏝":"desert island","🏞":"national park","🏟":"stadium","🏠":"house","🏡":"house with garden","🏢":"office building","🏣":"Japanese post office","🏤":"post office","🏥":"hospital","🏦":"bank","🏨":"hotel","🏩":"love hotel","🏪":"convenience store","🏫":"school","🏬":"department store","🏭":"factory","🏯":"Japanese castle","🏰":"castle","💈":"barber pole","💒":"wedding","💧":"droplet","💺":"seat","🔥":"fire","🕋":"kaaba","🕌":"mosque","🕍":"synagogue","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🗺":"world map","🗻":"mount fuji","🗼":"Tokyo tower","🗽":"Statue of Liberty","🗾":"map of Japan","🚀":"rocket","🚁":"helicopter","🚂":"locomotive","🚃":"railway car","🚄":"high-speed train","🚅":"bullet train","🚆":"train","🚇":"metro","🚈":"light rail","🚉":"station","🚊":"tram","🚋":"tram car","🚌":"bus","🚍":"oncoming bus","🚎":"trolleybus","🚏":"bus stop","🚐":"minibus","🚑":"ambulance","🚒":"fire engine","🚓":"police car","🚔":"oncoming police car","🚕":"taxi","🚖":"oncoming taxi","🚗":"automobile","🚘":"oncoming automobile","🚙":"sport utility vehicle","🚚":"delivery truck","🚛":"articulated lorry","🚜":"tractor","🚝":"monorail","🚞":"mountain railway","🚟":"suspension railway","🚠":"mountain cableway","🚡":"aerial tramway","🚢":"ship","🚤":"speedboat","🚥":"horizontal traffic light","🚦":"vertical traffic light","🚧":"construction","🚨":"police car light","🚲":"bicycle","🛎":"bellhop bell","🛑":"stop sign","🛕":"hindu temple","🛢":"oil drum","🛣":"motorway","🛤":"railway track","🛥":"motor boat","🛩":"small airplane","🛫":"airplane departure","🛬":"airplane arrival","🛰":"satellite","🛳":"passenger ship","🛴":"kick scooter","🛵":"motor scooter","🛶":"canoe","🛸":"flying saucer","🛹":"skateboard","🛺":"auto rickshaw","🦼":"motorized wheelchair","🦽":"manual wheelchair","🧭":"compass","🧱":"brick","🧳":"luggage","🪂":"parachute","🪐":"ringed planet"},"place-other":{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","♨":"hot springs","⚓":"anchor","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛱":"umbrella on ground","⛲":"fountain","⛴":"ferry","⛵":"sailboat","⛺":"tent","⛽":"fuel pump","✈":"airplane","❄":"snowflake","⭐":"star","🌀":"cyclone","🌁":"foggy","🌂":"closed umbrella","🌃":"night with stars","🌄":"sunrise over mountains","🌅":"sunrise","🌆":"cityscape at dusk","🌇":"sunset","🌈":"rainbow","🌉":"bridge at night","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","🎠":"carousel horse","🎡":"ferris wheel","🎢":"roller coaster","🎪":"circus tent","🏍":"motorcycle","🏎":"racing car","🏙":"cityscape","💈":"barber pole","💧":"droplet","💺":"seat","🔥":"fire","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🚀":"rocket","🚁":"helicopter","🚂":"locomotive","🚃":"railway car","🚄":"high-speed train","🚅":"bullet train","🚆":"train","🚇":"metro","🚈":"light rail","🚉":"station","🚊":"tram","🚋":"tram car","🚌":"bus","🚍":"oncoming bus","🚎":"trolleybus","🚏":"bus stop","🚐":"minibus","🚑":"ambulance","🚒":"fire engine","🚓":"police car","🚔":"oncoming police car","🚕":"taxi","🚖":"oncoming taxi","🚗":"automobile","🚘":"oncoming automobile","🚙":"sport utility vehicle","🚚":"delivery truck","🚛":"articulated lorry","🚜":"tractor","🚝":"monorail","🚞":"mountain railway","🚟":"suspension railway","🚠":"mountain cableway","🚡":"aerial tramway","🚢":"ship","🚤":"speedboat","🚥":"horizontal traffic light","🚦":"vertical traffic light","🚧":"construction","🚨":"police car light","🚲":"bicycle","🛎":"bellhop bell","🛑":"stop sign","🛢":"oil drum","🛣":"motorway","🛤":"railway track","🛥":"motor boat","🛩":"small airplane","🛫":"airplane departure","🛬":"airplane arrival","🛰":"satellite","🛳":"passenger ship","🛴":"kick scooter","🛵":"motor scooter","🛶":"canoe","🛸":"flying saucer","🛹":"skateboard","🛺":"auto rickshaw","🦼":"motorized wheelchair","🦽":"manual wheelchair","🧳":"luggage","🪂":"parachute","🪐":"ringed planet"},"place-religious":{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","♨":"hot springs","⚓":"anchor","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛩":"shinto shrine","⛪":"church","⛱":"umbrella on ground","⛲":"fountain","⛴":"ferry","⛵":"sailboat","⛺":"tent","⛽":"fuel pump","✈":"airplane","❄":"snowflake","⭐":"star","🌀":"cyclone","🌁":"foggy","🌂":"closed umbrella","🌃":"night with stars","🌄":"sunrise over mountains","🌅":"sunrise","🌆":"cityscape at dusk","🌇":"sunset","🌈":"rainbow","🌉":"bridge at night","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","🎠":"carousel horse","🎡":"ferris wheel","🎢":"roller coaster","🎪":"circus tent","🏍":"motorcycle","🏎":"racing car","🏙":"cityscape","💈":"barber pole","💧":"droplet","💺":"seat","🔥":"fire","🕋":"kaaba","🕌":"mosque","🕍":"synagogue","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🚀":"rocket","🚁":"helicopter","🚂":"locomotive","🚃":"railway car","🚄":"high-speed train","🚅":"bullet train","🚆":"train","🚇":"metro","🚈":"light rail","🚉":"station","🚊":"tram","🚋":"tram car","🚌":"bus","🚍":"oncoming bus","🚎":"trolleybus","🚏":"bus stop","🚐":"minibus","🚑":"ambulance","🚒":"fire engine","🚓":"police car","🚔":"oncoming police car","🚕":"taxi","🚖":"oncoming taxi","🚗":"automobile","🚘":"oncoming automobile","🚙":"sport utility vehicle","🚚":"delivery truck","🚛":"articulated lorry","🚜":"tractor","🚝":"monorail","🚞":"mountain railway","🚟":"suspension railway","🚠":"mountain cableway","🚡":"aerial tramway","🚢":"ship","🚤":"speedboat","🚥":"horizontal traffic light","🚦":"vertical traffic light","🚧":"construction","🚨":"police car light","🚲":"bicycle","🛎":"bellhop bell","🛑":"stop sign","🛕":"hindu temple","🛢":"oil drum","🛣":"motorway","🛤":"railway track","🛥":"motor boat","🛩":"small airplane","🛫":"airplane departure","🛬":"airplane arrival","🛰":"satellite","🛳":"passenger ship","🛴":"kick scooter","🛵":"motor scooter","🛶":"canoe","🛸":"flying saucer","🛹":"skateboard","🛺":"auto rickshaw","🦼":"motorized wheelchair","🦽":"manual wheelchair","🧳":"luggage","🪂":"parachute","🪐":"ringed planet"},"sky & weather":{"☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛱":"umbrella on ground","❄":"snowflake","⭐":"star","🌀":"cyclone","🌂":"closed umbrella","🌈":"rainbow","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","💧":"droplet","🔥":"fire","🪐":"ringed planet"},time:{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛱":"umbrella on ground","❄":"snowflake","⭐":"star","🌀":"cyclone","🌂":"closed umbrella","🌈":"rainbow","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","💧":"droplet","🔥":"fire","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🪐":"ringed planet"},"transport-air":{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛱":"umbrella on ground","✈":"airplane","❄":"snowflake","⭐":"star","🌀":"cyclone","🌂":"closed umbrella","🌈":"rainbow","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","💧":"droplet","💺":"seat","🔥":"fire","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🚀":"rocket","🚁":"helicopter","🚟":"suspension railway","🚠":"mountain cableway","🚡":"aerial tramway","🛎":"bellhop bell","🛩":"small airplane","🛫":"airplane departure","🛬":"airplane arrival","🛰":"satellite","🛸":"flying saucer","🧳":"luggage","🪂":"parachute","🪐":"ringed planet"},"transport-ground":{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","⚓":"anchor","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛱":"umbrella on ground","⛴":"ferry","⛵":"sailboat","⛽":"fuel pump","✈":"airplane","❄":"snowflake","⭐":"star","🌀":"cyclone","🌂":"closed umbrella","🌈":"rainbow","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","🏍":"motorcycle","🏎":"racing car","💧":"droplet","💺":"seat","🔥":"fire","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🚀":"rocket","🚁":"helicopter","🚂":"locomotive","🚃":"railway car","🚄":"high-speed train","🚅":"bullet train","🚆":"train","🚇":"metro","🚈":"light rail","🚉":"station","🚊":"tram","🚋":"tram car","🚌":"bus","🚍":"oncoming bus","🚎":"trolleybus","🚏":"bus stop","🚐":"minibus","🚑":"ambulance","🚒":"fire engine","🚓":"police car","🚔":"oncoming police car","🚕":"taxi","🚖":"oncoming taxi","🚗":"automobile","🚘":"oncoming automobile","🚙":"sport utility vehicle","🚚":"delivery truck","🚛":"articulated lorry","🚜":"tractor","🚝":"monorail","🚞":"mountain railway","🚟":"suspension railway","🚠":"mountain cableway","🚡":"aerial tramway","🚢":"ship","🚤":"speedboat","🚥":"horizontal traffic light","🚦":"vertical traffic light","🚧":"construction","🚨":"police car light","🚲":"bicycle","🛎":"bellhop bell","🛑":"stop sign","🛢":"oil drum","🛣":"motorway","🛤":"railway track","🛥":"motor boat","🛩":"small airplane","🛫":"airplane departure","🛬":"airplane arrival","🛰":"satellite","🛳":"passenger ship","🛴":"kick scooter","🛵":"motor scooter","🛶":"canoe","🛸":"flying saucer","🛹":"skateboard","🛺":"auto rickshaw","🦼":"motorized wheelchair","🦽":"manual wheelchair","🧳":"luggage","🪂":"parachute","🪐":"ringed planet"},"transport-water":{"⌚":"watch","⌛":"hourglass done","⏰":"alarm clock","⏱":"stopwatch","⏲":"timer clock","⏳":"hourglass not done","☀":"sun","☁":"cloud","☂":"umbrella","☃":"snowman","☄":"comet","☔":"umbrella with rain drops","⚓":"anchor","⚡":"high voltage","⛄":"snowman without snow","⛅":"sun behind cloud","⛈":"cloud with lightning and rain","⛱":"umbrella on ground","⛴":"ferry","⛵":"sailboat","✈":"airplane","❄":"snowflake","⭐":"star","🌀":"cyclone","🌂":"closed umbrella","🌈":"rainbow","🌊":"water wave","🌌":"milky way","🌑":"new moon","🌒":"waxing crescent moon","🌓":"first quarter moon","🌔":"waxing gibbous moon","🌕":"full moon","🌖":"waning gibbous moon","🌗":"last quarter moon","🌘":"waning crescent moon","🌙":"crescent moon","🌚":"new moon face","🌛":"first quarter moon face","🌜":"last quarter moon face","🌝":"full moon face","🌞":"sun with face","🌟":"glowing star","🌠":"shooting star","🌡":"thermometer","🌤":"sun behind small cloud","🌥":"sun behind large cloud","🌦":"sun behind rain cloud","🌧":"cloud with rain","🌨":"cloud with snow","🌩":"cloud with lightning","🌪":"tornado","🌫":"fog","🌬":"wind face","💧":"droplet","💺":"seat","🔥":"fire","🕐":"one o’clock","🕑":"two o’clock","🕒":"three o’clock","🕓":"four o’clock","🕔":"five o’clock","🕕":"six o’clock","🕖":"seven o’clock","🕗":"eight o’clock","🕘":"nine o’clock","🕙":"ten o’clock","🕚":"eleven o’clock","🕛":"twelve o’clock","🕜":"one-thirty","🕝":"two-thirty","🕞":"three-thirty","🕟":"four-thirty","🕠":"five-thirty","🕡":"six-thirty","🕢":"seven-thirty","🕣":"eight-thirty","🕤":"nine-thirty","🕥":"ten-thirty","🕦":"eleven-thirty","🕧":"twelve-thirty","🕰":"mantelpiece clock","🚀":"rocket","🚁":"helicopter","🚟":"suspension railway","🚠":"mountain cableway","🚡":"aerial tramway","🚢":"ship","🚤":"speedboat","🛎":"bellhop bell","🛥":"motor boat","🛩":"small airplane","🛫":"airplane departure","🛬":"airplane arrival","🛰":"satellite","🛳":"passenger ship","🛶":"canoe","🛸":"flying saucer","🧳":"luggage","🪂":"parachute","🪐":"ringed planet"}}};;

// uses twemoji (c) Twitter
// https://github.com/WebReflection/emoji-essential/blob/master/LICENSE
const emoji = {};
let didProcessEmoji = {};
Object.keys(emojiEssential).forEach(group => {
    emoji[group] = {
        glyph: [],
        description: []
    };
    Object.keys(emojiEssential[group]).forEach(sub => {
        Object.keys(emojiEssential[group][sub]).forEach(key => {
            if (!didProcessEmoji[key]) {
                didProcessEmoji[key] = true;
                emoji[group].glyph.push(key);
                emoji[group].description.push(emojiEssential[group][sub][key]);
            }
        });
    });
});

let classicSmiles = {};
classicSmiles[":)"] = "smile";
classicSmiles[";)"] = "wink";
classicSmiles[":D"] = "bigsmile";
classicSmiles[":("] = "sad";
classicSmiles[":S"] = "confused";
classicSmiles[":\'("] = "cry";
classicSmiles[":O"] = "surprised";
classicSmiles[":P"] = "tongue";
classicSmiles[":cool:"] = "cool";
classicSmiles[":smirk:"] = "smirk";
classicSmiles["(:"] = "smileupside";
classicSmiles[":thinking:"] = "thinking";
classicSmiles[":$"] = "embarassed";
classicSmiles[":|"] = "stunned";
classicSmiles[":@"] = "angry";
classicSmiles["8-|"] = "geek";
classicSmiles["(L)"] = "love";
classicSmiles[":heart:"] = "heart";
classicSmiles[":wait:"] = "wait";
classicSmiles[":build:"] = "build";
classicSmiles[":done:"] = "done";
classicSmiles[":undone:"] = "undone";
classicSmiles["+o("] = "sick";
classicSmiles["(BRB)"] = "brb";
classicSmiles["(Y)"] = "thumbsup";
classicSmiles[":thdown:"] = "thdown";
classicSmiles["=P"] = "drool";
classicSmiles[":-W"] = "waiting";
classicSmiles[":bronze:"] = "bronzen";
classicSmiles[":silver:"] = "silvern";
classicSmiles[":gold:"] = "goldn";
classicSmiles[":award:"] = "awardn";


let classics = Object.keys(classicSmiles);

emoji['Classics'] = {
    glyph: classics,
    description: classics
};

let U200D = String.fromCharCode(0x200D);
let UFE0Fg = /\uFE0F/g;
function imageFromText(rawText) {
    let img = document.createElement('img');
    img.innerText = rawText;
    if (rawText[0] == ':' || rawText[0] == ';' || rawText[0] == '(' || rawText[0] == '=' || rawText[0] == '/' || rawText[0] == '+' || rawText[0] == '|' || rawText[0] == '-' || rawText[0] == 'l' || rawText[0] == '8') { // legacy
        img.setAttribute('src', 'https://images.mania-exchange.com/emojis/' + classicSmiles[rawText] + '.png');
    } else { // emoji
        let codepoint = toCodePoint(rawText.indexOf(U200D) < 0 ? rawText.replace(UFE0Fg, '') : rawText);

        img.innerText = rawText;
        img.setAttribute('src', 'https://images.mania-exchange.com/emojis/twemoji/' + codepoint + '.png');
        img.setAttribute('class', 'emoji');
    }

    return img;
}

function wait(t) {
    return new Promise(function (resolve) {
        setTimeout(resolve, t);
    });
}


let popup = null;
function hideWindow() {
    if (popup) {
        popup.parentElement.removeChild(popup);
        popup = null;
    }
}

// https://stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript
function getScrollbarWidth() {
    // Creating invisible container
    const outer = document.createElement('div');
    outer.style.visibility = 'hidden';
    outer.style.overflow = 'scroll'; // forcing scrollbar to appear
    outer.style.msOverflowStyle = 'scrollbar'; // needed for WinJS apps
    document.body.appendChild(outer);

    // Creating inner element and placing it in the container
    const inner = document.createElement('div');
    outer.appendChild(inner);

    // Calculating difference between container's full width and the child width
    const scrollbarWidth = (outer.offsetWidth - inner.offsetWidth);

    // Removing temporary elements from the DOM
    outer.parentNode.removeChild(outer);

    return scrollbarWidth;
}

function createWindow(template, attachTo, width, height, relPos) {
    hideWindow();

    let rect = bbemoji.getBoundingClientRect();
    let container = document.createElement('div');
    let finalwidth = width + getScrollbarWidth();
    container.style.position = 'absolute';
    container.style.width = finalwidth + 'px';
    container.style.height = height + 'px';
    container.style.left = (((rect.right + rect.left) * 0.5 - finalwidth * 0.5) + document.documentElement.scrollLeft) + 'px';
    container.style.top = (rect.bottom + 5 + document.documentElement.scrollTop) + 'px';
    container.style.zIndex = 1000;
    container.style.borderRadius = '4px';
    container.style.backgroundColor = '#fff';
    container.style.border = '1px solid #ccc';
    container.style.boxShadow = '0 0 10px #0003, 0 0 100px #0002';
    container.style.padding = '10px';
    container.style.overflowY = 'scroll';
    container.classList = 'emojiWindow';
    document.body.appendChild(container);

    for (let ch of template.children) {
        let contents = ch.cloneNode(true);
        container.appendChild(contents);
    }

    container.addEventListener('click', function (e) {
        e.stopPropagation();
    });

    popup = container;

    return container;
}

window.addEventListener('click', function () {
    hideWindow();
})

let states = {};

let muStyles = ['b', 'i', 's', 'u'];
let tagsWithContents = ['b', 'i', 's', 'u', 'left', 'center', 'right', 'url', 'quote', 'list', 'item'];
let tagsWithArg = ['url', 'quote'];

function addToActiveStyle(styles, toAdd) {
    let newStyles = [...styles];
    if (styles.includes(toAdd)) return newStyles;

    newStyles.push(toAdd);
    newStyles.sort();
    return newStyles;
}

function trfStyleName(styleName) {
    return styleName;
}

let css = '';

for (let styleFlags = 0; styleFlags < (2 << 4); ++styleFlags) {
    let activeStyles = [];
    for (let i = 0; i < 4; ++i) {
        if (styleFlags & (2 << i)) {
            activeStyles.push(muStyles[i]);
        }
    }

    let activeStylesTokenBit = activeStyles.join('');

    let state = [
        { regex: /(\[img\])(.*?)(\[\/img\])/, token: ['mukeyword', 'mutagvar', 'mukeyword'] },
        { regex: /(\[user\])(.*?)(\[\/user\])/, token: ['mukeyword', 'mutagvar', 'mukeyword'] },
        { regex: /(\[track\])(.*?)(\[\/track\])/, token: ['mukeyword', 'mutagvar', 'mukeyword'] },
        { regex: /(\[track=)(short)(\])(.*?)(\[\/track\])/, token: ['mukeyword', 'mutagvar', 'mukeyword', 'mutagvar', 'mukeyword'] },
        { regex: /(\[track=)(full)(\])(.*?)(\[\/track\])/, token: ['mukeyword', 'mutagvar', 'mukeyword', 'mutagvar', 'mukeyword'] },
    ];

    for (let style of tagsWithContents) {
        let newActiveStyles = activeStyles;
        if (muStyles.includes(style)) newActiveStyles = addToActiveStyle(activeStyles, style);
        state.push({ regex: RegExp('\\[' + style + '\\]'), token: 'mudstyle-' + newActiveStyles.join(''), push: trfStyleName('markup-' + newActiveStyles.join('') + '-' + style) });
        if (tagsWithArg.includes(style)) {
            state.push({ regex: RegExp('(\\[' + style + '=)(.*?)(\\])'), token: ['mudstyle-' + newActiveStyles.join(''), 'mutagvar', 'mudstyle-' + newActiveStyles.join('')], push: trfStyleName('markup-' + newActiveStyles.join('') + '-' + style) });
        }
    }

    state.push({ regex: /(\[)(\[.*?\])(\])/, token: ['mukeyword', 'mustyle-' + activeStylesTokenBit, 'mukeyword'] })
    state.push({ regex: /./, token: 'mustyle-' + activeStylesTokenBit })

    if (activeStyles.length === 0) {
        states['start'] = state;
    }

    for (let activeStyle of tagsWithContents) {
        let styleName = trfStyleName('markup-' + activeStylesTokenBit + '-' + activeStyle);

        let innerState = [
            { regex: RegExp('\\[\\/' + activeStyle + '\\]'), token: 'mudstyle-' + activeStylesTokenBit, pop: true },
        ]
        for (let matcher of state) {
            innerState.push(matcher);
        }

        states[styleName] = innerState;
    }

    let thisCss = '';

    if (activeStyles.includes('b')) {
        thisCss += 'font-weight:bold;'
    }
    if (activeStyles.includes('i')) {
        thisCss += 'font-style:italic;'
    }
    if (activeStyles.includes('u') && activeStyles.includes('s')) {
        thisCss += 'text-decoration:underline line-through;'
    } else if (activeStyles.includes('u')) {
        thisCss += 'text-decoration:underline;'
    } else if (activeStyles.includes('s')) {
        thisCss += 'text-decoration:line-through;'
    }

    css += '.cm-mustyle-' + activeStylesTokenBit + ' {' + thisCss + '}';
    css += '.cm-mudstyle-' + activeStylesTokenBit + ' {color:#aaa;}';
}

//let cssContainer = document.getElementById('cm-genstyles');
//cssContainer.innerHTML = css;

CodeMirror.defineSimpleMode("simplemode", states);

// from twemoji / MIT / https://github.com/twitter/twemoji
function toCodePoint(unicodeSurrogates, sep) {
    var
        r = [],
        c = 0,
        p = 0,
        i = 0;
    while (i < unicodeSurrogates.length) {
        c = unicodeSurrogates.charCodeAt(i++);
        if (p) {
            r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
            p = 0;
        } else if (0xD800 <= c && c <= 0xDBFF) {
            p = c;
        } else {
            r.push(c.toString(16));
        }
    }
    return r.join(sep || '-');
}

function createBbInput(input) {
    let headerHTML = '<template id="emojipicker"></template><div class="bbheader"><a title="Bold" id="bbbold"><i class="fas fa-bold"></i></a><a title="Italic" id="bbitalic"><i class="fas fa-italic"></i></a><a title="Underline" id="bbunderline"><i class="fas fa-underline"></i></a><a title="Strikethrough" id="bbstrikethrough"><i class="fas fa-strikethrough"></i></a><div class="bbsep"></div><a title="Align left" id="bbalignleft"><i class="fas fa-align-left"></i></a><a title="Align center" id="bbaligncenter"><i class="fas fa-align-center"></i></a><a title="Align right" id="bbalignright"><i class="fas fa-align-right"></i></a><div class="bbsep"></div><a title="Link" id="bblink"><i class="fas fa-link"></i></a><a title="Image" id="bbimage"><i class="fas fa-image"></i></a><a title="List" id="bblist"><i class="fas fa-list-ul"></i></a><a title="List item" id="bbitem"><i class="fas fa-circle"></i></a><a title="Quote" id="bbquote"><i class="fas fa-quote-right"></i></a><a title="Horizontal Line" id="bbhr"><i class="fas fa-grip-lines"></i></a><div class="bbsep"></div><a title="Link to user" id="bbuser"><i class="fas fa-user"></i></a><a title="Link to map" id="bbtrack1"><i class="fas fa-route"></i></a><a title="Link to map (short format)" class="bbbutton-wide" id="bbtrack2"><i class="fas fa-route"></i>&nbsp;(short)</a><a title="Link to map (full format)" class="bbbutton-wide" id="bbtrack3"><i class="fas fa-route"></i>&nbsp;(full)</a><a title="MX Text" id="bbmx"><img src="https://images.mania.exchange/logos/mx/square_sm.png" /></a><a title="TMX Text" id="bbtmx"><img src="https://images.mania.exchange/logos/tmx/square_sm.png" /></a><div class="bbsep"></div><a title="Emojis" class="bbbutton-wide" id="bbemoji"><i class="fas fa-smile"></i>&nbsp;Emojis</a></div>';

    let container = document.createElement('div');
    container.setAttribute('class', 'bbeditor');
    container.innerHTML = headerHTML;
    input.parentElement.insertBefore(container, input);
    container.appendChild(input);

    let emojiPicker = document.getElementById('emojipicker');
    async function initEmoji() {

        let searchBox = document.createElement('input');
        searchBox.setAttribute('type', 'text');
        searchBox.setAttribute('placeholder', 'Search');
        searchBox.style.width = '100%';

        emojiPicker.appendChild(searchBox);

        let order = ['Classics', 'Smileys & Emotion', 'People & Body', 'Animals & Nature', 'Activities', 'Travel & Places', 'Food & Drink', 'Objects', 'Symbols', 'Flags'];
        for (let groupidx = 0; groupidx < order.length; ++groupidx) {
            let group = order[groupidx];
            let inner = emoji[group];
            let header = document.createElement('h5');
            header.innerText = group;
            emojiPicker.appendChild(header);

            let loadingIndicator = document.createElement('div');
            loadingIndicator.innerText = 'Loading, re-open dialog to try again.';
            emojiPicker.appendChild(loadingIndicator);

            let emojiCtr = document.createElement('div');
            emojiCtr.classList.add('emojilist');
            let loadIncr = 20;
            for (let row = 0; row < Math.ceil(inner.glyph.length / loadIncr); ++row) {
                for (let i = row * loadIncr; i < Math.min((row + 1) * loadIncr, inner.glyph.length); ++i) {
                    let button = document.createElement('a');
                    button.setAttribute('emoji', inner.glyph[i]);
                    button.setAttribute('title', inner.description[i]);
                    button.classList.add('emojibutton');
                    let img = imageFromText(inner.glyph[i]);
                    button.appendChild(img);

                    emojiCtr.appendChild(button);
                }

                await wait(1);
            }
            emojiPicker.removeChild(loadingIndicator);
            emojiPicker.appendChild(emojiCtr);
        }
    }
    setTimeout(initEmoji, 1);


    let bbemoji = document.getElementById('bbemoji');
    bbemoji.addEventListener('click', function (e) {
        let popup = createWindow(emojiPicker, bbemoji, 20 + 24 * 10, 24 * 10, 'cd');
        popup.addEventListener('click', function (e) {
            let itr = e.target;
            while (itr) {
                if (itr.hasAttribute('emoji')) {
                    cm.focus();
                    cm.replaceSelection(itr.getAttribute('emoji') + ' ');
                    break;
                }
                itr = itr.parentElement;
            }
        });

        let allEmojiButtons = popup.querySelectorAll('a[emoji]');
        let emojiQueryMap = [];
        for (let button of allEmojiButtons) {
            emojiQueryMap.push([button, button.getAttribute('title')]);
        }

        let textField = popup.querySelector('input');
        textField.addEventListener('input', function (e) {
            let query = textField.value;
            for (let [button, matchstr] of emojiQueryMap) {
                button.style.display = matchstr.includes(query) ? null : 'none';
            }
        });

        e.stopPropagation();
    });

    function wrapSelection(tagstart, tagend, defaulttext) {
        cm.focus();
        let text = cm.getSelection();
        if (text.length > 0) {
            cm.replaceSelection(tagstart + text + tagend);
        } else {
            cm.replaceSelection(tagstart + defaulttext + tagend);
        }
    }

    document.getElementById('bbbold').addEventListener('click', () => wrapSelection('[b]', '[/b]', 'Text'));
    document.getElementById('bbitalic').addEventListener('click', () => wrapSelection('[i]', '[/i]', 'Text'));
    document.getElementById('bbunderline').addEventListener('click', () => wrapSelection('[u]', '[/u]', 'Text'));
    document.getElementById('bbstrikethrough').addEventListener('click', () => wrapSelection('[s]', '[/s]', 'Text'));
    document.getElementById('bbalignleft').addEventListener('click', () => wrapSelection('[align=left]', '[/align]', 'Text'));
    document.getElementById('bbaligncenter').addEventListener('click', () => wrapSelection('[align=center]', '[/align]', 'Text'));
    document.getElementById('bbalignright').addEventListener('click', () => wrapSelection('[align=right]', '[/align]', 'Text'));
    document.getElementById('bblink').addEventListener('click', () => wrapSelection('[url=' + prompt("Enter a URL", "") + ']', '[/url]', 'Text'));
    document.getElementById('bbimage').addEventListener('click', () => wrapSelection('[img]', '[/img]', prompt("Enter an image URL", "")));
    document.getElementById('bblist').addEventListener('click', () => wrapSelection('[list]\n[item]', '\n[/list]', 'Item text'));
    document.getElementById('bbitem').addEventListener('click', () => wrapSelection('[item]', '', 'Item'));
    document.getElementById('bbquote').addEventListener('click', () => wrapSelection('[quote]', '[/quote]', 'Text'));
    document.getElementById('bbhr').addEventListener('click', () => wrapSelection('[hr]', '', ''));
    document.getElementById('bbuser').addEventListener('click', () => wrapSelection('[user]', '[/user]', prompt("Enter a UserID", "")));
    document.getElementById('bbtrack1').addEventListener('click', () => wrapSelection('[track]', '[/track]', prompt("Enter a MapID", "")));
    document.getElementById('bbtrack2').addEventListener('click', () => wrapSelection('[track=short]', '[/track]', prompt("Enter a MapID", "")));
    document.getElementById('bbtrack3').addEventListener('click', () => wrapSelection('[track=full]', '[/track]', prompt("Enter a MapID", "")));
    document.getElementById('bbmx').addEventListener('click', () => wrapSelection('[mx]', '', ''));
    document.getElementById('bbtmx').addEventListener('click', () => wrapSelection('[tmx]', '', ''));

    
    var cm = CodeMirror.fromTextArea(input, {
        lineWrapping: true,
        inputStyle: 'contenteditable'
    });
    cm.getInputField().spellcheck = true

    let emojiRegex = /:\)|;\)|:D|:\(|:'\(|:S|:\\|:O|:P|:cool:|:smirk:|\(:|:thinking:|:\||:\$|:\@|8\-\||\(L\)|:heart:|:wait:|:build:|:done:|:undone:|\+o\(|\(BRB\)|\(Y\)|:thdown:|=P|:\-W|:bronze:|:silver:|:gold:|:award:|\(H\)|(?:\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d])|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5\udeeb\udeec\udef4-\udefa\udfe0-\udfeb]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd1d\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd71\udd73-\udd76\udd7a-\udda2\udda5-\uddaa\uddae-\uddb4\uddb7\uddba\uddbc-\uddca\uddd0\uddde-\uddff\ude70-\ude73\ude78-\ude7a\ude80-\ude82\ude90-\ude95]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g;

    function updateEmoji(from, to) {
        for (let line = from; line < to; ++line) {
            for (let marker of cm.findMarks({ line: line, ch: 0 }, { line: line + 1, ch: 0 })) {
                marker.clear();
            }

            var lineContents = cm.getLine(line);
            let result;
            emojiRegex.lastIndex = 0;
            while (result = emojiRegex.exec(lineContents)) {
                let matchStart = result.index;
                let matchEnd = emojiRegex.lastIndex;

                let img = imageFromText(result[0]);
                let marker = cm.markText({ line: line, ch: matchStart }, { line: line, ch: matchEnd }, { replacedWith: img, handleMouseEvents: true, selectLeft: true, selectRight: true });
            }
        }
    }

    updateEmoji(0, cm.lineCount());

    cm.on('change', function (cm, changeObj) {
        updateEmoji(changeObj.from.line, changeObj.from.line + changeObj.text.length);
    });

}

let elems = document.querySelectorAll('textarea.EditBox')
for (let elem of elems) {
    createBbInput(elem);
}
;
if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function (searchElement /*, fromIndex */) {
		"use strict";
		if (this === void 0 || this === null) {
			throw new TypeError();
		}
		var t = Object(this);
		var len = t.length >>> 0;
		if (len === 0) {
			return -1;
		}
		var n = 0;
		if (arguments.length > 0) {
			n = Number(arguments[1]);
			if (n !== n) { // shortcut for verifying if it's NaN
				n = 0;
			} else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
				n = (n > 0 || -1) * Math.floor(Math.abs(n));
			}
		}
		if (n >= len) {
			return -1;
		}
		var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
		for (; k < len; k++) {
			if (k in t && t[k] === searchElement) {
				return k;
			}
		}
		return -1;
	}
};
