<commit_before>import { AbstractTheme } from '../theme'

export var bootstrap4Theme = AbstractTheme.extend({
  /* Theme config options that allows changing various aspects of the output */
  options: {
    disable_theme_rules: false,
    input_size: 'normal', // Size of input and select elements. "small", "normal", "large"
    custom_forms: false // use twbs custom form stylings
  },
  /* Custom stylesheet rules. format: "selector" : "CSS rules" */
  rules: {
    '.jsoneditor-twbs4-text-button': 'background: none;padding: 0;border: 0',
    'td>.form-group': 'margin-bottom: 0',
    'div[data-schemaid="root"]:after': 'position:relative;color:red;margin:10px 0;font-weight:600;display:block;width:100%;text-align:center;content:"This is an old JSON-Editor 1.x Theme and might not display elements correctly when used with the 2.x version"'
  },

  getSelectInput: function (options, multiple) {
    var el = this._super(options)
    el.classList.add('form-control')

    if (this.options.custom_forms === false) {
      if (this.options.input_size === 'small') el.classList.add('form-control-sm')
      if (this.options.input_size === 'large') el.classList.add('form-control-lg')
    } else {
      el.classList.remove('form-control')
      el.classList.add('custom-select')
      if (this.options.input_size === 'small') el.classList.add('custom-select-sm')
      if (this.options.input_size === 'large') el.classList.add('custom-select-lg')
    }

    return el
  },

  setGridColumnSize: function (el, size, offset) {
    el.classList.add('col-md-' + size)

    if (offset) {
      el.classList.add('offset-md-' + offset)
    }
  },

  afterInputReady: function (input) {
    if (input.controlgroup) return

    input.controlgroup = this.closest(input, '.form-group')
  },

  getTextareaInput: function () {
    var el = document.createElement('textarea')
    el.classList.add('form-control')
    if (this.options.input_size === 'small') el.classList.add('form-control-sm')
    if (this.options.input_size === 'large') el.classList.add('form-control-lg')
    return el
  },

  getRangeInput: function (min, max, step) {
    var el = this._super(min, max, step)

    if (this.options.custom_forms === true) {
      el.classList.remove('form-control')
      el.classList.add('custom-range')
    }

    return el
  },

  getFormInputField: function (type) {
    var el = this._super(type)
    if (type !== 'checkbox' && type !== 'radio') {
      el.classList.add('form-control')
      if (this.options.input_size === 'small') el.classList.add('form-control-sm')
      if (this.options.input_size === 'large') el.classList.add('form-control-lg')
    }
    return el
  },

  getFormControl: function (label, input, description, infoText) {
    var group = document.createElement('div')
    group.classList.add('form-group')

    if (label && (input.type === 'checkbox' || input.type === 'radio')) {
      var check = document.createElement('div')

      if (this.options.custom_forms === false) {
        check.classList.add('form-check')
        input.classList.add('form-check-input')
        label.classList.add('form-check-label')
      } else {
        check.classList.add('custom-control')
        input.classList.add('custom-control-input')
        label.classList.add('custom-control-label')

        if (input.type === 'checkbox') {
          check.classList.add('custom-checkbox')
        } else {
          check.classList.add('custom-radio')
        }
      }

      check.appendChild(input)
      check.appendChild(label)
      if (infoText) check.appendChild(infoText)

      group.appendChild(check)
    } else {
      if (label) {
        group.appendChild(label)

        if (infoText) group.appendChild(infoText)
      }

      group.appendChild(input)
    }

    if (description) {
      group.appendChild(description)
    }

    return group
  },

  getInfoButton: function (text) {
    var button = document.createElement('button') // shoud be a <button> but no fitting tbws style...
    button.type = 'button'
    button.classList.add('ml-3', 'jsoneditor-twbs4-text-button')
    button.setAttribute('data-toggle', 'tooltip')
    button.setAttribute('data-placement', 'auto')
    button.title = text

    var icon = document.createTextNode('ⓘ')
    button.appendChild(icon)

    var tooltip = document.createElement('div')
    tooltip.classList.add('tooltip', 'bs-tooltip-top')
    tooltip.setAttribute('role', 'tooltip')

    if (window.jQuery) {
      // arrow is only usefull if positioned by popover.js
      var arrow = document.createElement('div')
      arrow.classList.add('arrow')
      tooltip.appendChild(arrow)

      window.jQuery(button).tooltip()
    } else {
      // better fallback for non-js / no twbs js situtions?
      // for now just rely on browser native [title]
    }

    return button
  },

  /**
   * Generates a checkbox...
   *
   * Overwriten from master theme to get rid of inline styles.
   */
  getCheckbox: function () {
    var el = this.getFormInputField('checkbox')
    return el
  },

  /**
   * Multiple checkboxes in a row.
   *
   */
  getMultiCheckboxHolder: function (controls, label, description, infoText) {
    var el = document.createElement('div')
    el.classList.add('form-group')

    if (label) {
      el.appendChild(label)

      if (infoText) {
        label.appendChild(infoText)
      }
    }

    // for inline view we need an container so it doesnt wrap in the "row" of the
    var container = document.createElement('div')

    for (var i in controls) {
      if (!controls.hasOwnProperty(i)) {
        continue
      }

      // controls are already parsed by getFormControl() so they have an .form-group
      // wrapper we need to get rid of...
      var ctrl = controls[i].firstChild

      // we don't know if this should be an normal / compact view
      /* if (this.options.custom_forms === false) {
        ctrl.classList.add('form-check-inline')
      } else {
        ctrl.classList.add('custom-control-inline')
      } */

      container.appendChild(ctrl)
    }

    el.appendChild(container)

    if (description) el.appendChild(description)

    return el
  },

  /**
   * Single radio element
   */
  getFormRadio: function (attributes) {
    var el = this.getFormInputField('radio')

    for (var key in attributes) {
      el.setAttribute(key, attributes[key])
    }

    if (this.options.custom_forms === false) {
      el.classList.add('form-check-input')
    } else {
      el.classList.add('custom-control-input')
    }

    return el
  },

  /**
   * Add the  for the single radio from getFormRadio()
   *
   */
  getFormRadioLabel: function (text, req) {
    var el = document.createElement('label')

    if (this.options.custom_forms === false) {
      el.classList.add('form-check-label')
    } else {
      el.classList.add('custom-control-label')
    }

    el.appendChild(document.createTextNode(text))
    return el
  },

  /**
   * Stack the radios from getFormRadio()/getFormRadioLabel()
   *
   */
  getFormRadioControl: function (label, input, compact) {
    var el = document.createElement('div')

    if (this.options.custom_forms === false) {
      el.classList.add('form-check')
    } else {
      el.classList.add('custom-control', 'custom-radio')
    }

    el.appendChild(input)
    el.appendChild(label)

    if (compact) {
      if (this.options.custom_forms === false) {
        el.classList.add('form-check-inline')
      } else {
        el.classList.add('custom-control-inline')
      }
    }

    return el
  },

  getIndentedPanel: function () {
    var el = document.createElement('div')
    el.classList.add('card', 'card-body', 'bg-light', 'mb-3')

    // for better twbs card styling we should be able to return a nested div

    return el
  },

  getFormInputDescription: function (text) {
    var el = document.createElement('small')
    el.classList.add('form-text')

    if (window.DOMPurify) {
      el.innerHTML = window.DOMPurify.sanitize(text)
    } else {
      el.textContent = this.cleanText(text)
    }

    return el
  },

  getHeader: function (text) {
    // var cardHeader = document.createElement('div')
    // cardHeader.classList.add('card-header')

    var el = document.createElement('h3')
    el.classList.add('card-title')

    if (typeof text === 'string') {
      el.textContent = text
    } else {
      text.classList.add('pr-3')
      el.appendChild(text)
    }

    // cardHeader.appendChild(el)

    return el
  },
  getHeaderButtonHolder: function () {
    var el = this.getButtonHolder()

    return el
  },
  getButtonHolder: function () {
    // todo: we dont want to use div.btn-group until we can wrap all buttons inside it
    // single button inside a group get's it's corners cut off
    var el = document.createElement('span')
    // el.classList.add('btn-group')
    // el.classList.add('ml-2')
    return el
  },
  getButton: function (text, icon, title) {
    var el = this._super(text, icon, title)
    el.classList.add('btn', 'btn-secondary', 'btn-sm', 'mr-2')
    return el
  },

  getTable: function () {
    var el = document.createElement('table')
    el.classList.add('table', 'table-bordered', 'table-sm')
    return el
  },

  /**
   * input validation on <input>
   */
  addInputError: function (input, text) {
    if (!input.controlgroup) return

    input.classList.add('is-invalid')

    if (!input.errmsg) {
      input.errmsg = document.createElement('p')
      input.errmsg.classList.add('invalid-feedback')
      input.controlgroup.appendChild(input.errmsg)
    } else {
      input.errmsg.style.display = ''
    }

    input.errmsg.textContent = text
  },
  removeInputError: function (input) {
    if (!input.errmsg) return
    input.errmsg.style.display = 'none'
    input.classList.remove('is-invalid')
  },

  getTabHolder: function (propertyName) {
    var el = document.createElement('div')
    var pName = (typeof propertyName === 'undefined') ? '' : propertyName
    el.innerHTML = "

"
    el.classList.add('row')
    return el
  },
  addTab: function (holder, tab) {
    holder.children[0].children[0].appendChild(tab)
  },
  getTabContentHolder: function (tabHolder) {
    return tabHolder.children[1].children[0]
  },

  getTopTabHolder: function (propertyName) {
    var pName = (typeof propertyName === 'undefined') ? '' : propertyName

    var el = document.createElement('div')
    el.classList.add('card')
    el.innerHTML = "

"

    return el
  },
  getTab: function (text, tabId) {
    var liel = document.createElement('li')
    liel.classList.add('nav-item')

    var ael = document.createElement('a')
    ael.classList.add('nav-link')
    ael.setAttribute('href', '#' + tabId)
    ael.setAttribute('data-toggle', 'tab')
    ael.appendChild(text)

    liel.appendChild(ael)

    return liel
  },
  getTopTab: function (text, tabId) {
    var el = document.createElement('li')
    el.classList.add('nav-item')

    var a = document.createElement('a')
    a.classList.add('nav-link')
    a.setAttribute('href', '#' + tabId)
    a.setAttribute('data-toggle', 'tab')
    a.appendChild(text)

    el.appendChild(a)

    return el
  },
  getTabContent: function () {
    var el = document.createElement('div')
    el.classList.add('tab-pane')
    el.setAttribute('role', 'tabpanel')
    return el
  },
  getTopTabContent: function () {
    var el = document.createElement('div')
    el.classList.add('tab-pane')
    el.setAttribute('role', 'tabpanel')
    return el
  },
  markTabActive: function (row) {
    row.tab.firstChild.classList.add('active')

    if (typeof row.rowPane !== 'undefined') {
      row.rowPane.classList.add('active')
    } else {
      row.container.classList.add('active')
    }
  },
  markTabInactive: function (row) {
    row.tab.firstChild.classList.remove('active')

    if (typeof row.rowPane !== 'undefined') {
      row.rowPane.classList.remove('active')
    } else {
      row.container.classList.remove('active')
    }
  },

  addTopTab: function (holder, tab) {
    holder.children[0].children[0].appendChild(tab)
  },

  getTopTabContentHolder: function (tabHolder) {
    return tabHolder.children[1].children[0]
  },

  getProgressBar: function () {
    var min = 0
    var max = 100
    var start = 0

    var container = document.createElement('div')
    container.classList.add('progress')

    var bar = document.createElement('div')
    bar.classList.add('progress-bar')
    bar.setAttribute('role', 'progressbar')
    bar.setAttribute('aria-valuenow', start)
    bar.setAttribute('aria-valuemin', min)
    bar.setAttribute('aria-valuenax', max)
    bar.innerHTML = start + '%'
    container.appendChild(bar)

    return container
  },
  updateProgressBar: function (progressBar, progress) {
    if (!progressBar) return

    var bar = progressBar.firstChild
    var percentage = progress + '%'
    bar.setAttribute('aria-valuenow', progress)
    bar.style.width = percentage
    bar.innerHTML = percentage
  },
  updateProgressBarUnknown: function (progressBar) {
    if (!progressBar) return

    var bar = progressBar.firstChild
    progressBar.classList.add('progress', 'progress-striped', 'active')
    bar.removeAttribute('aria-valuenow')
    bar.style.width = '100%'
    bar.innerHTML = ''
  },
  getInputGroup: function (input, buttons) {
    if (!input) return

    var inputGroupContainer = document.createElement('div')
    inputGroupContainer.classList.add('input-group')
    inputGroupContainer.appendChild(input)

    var inputGroup = document.createElement('div')
    inputGroup.classList.add('input-group-prepend')
    inputGroupContainer.appendChild(inputGroup)

    for (var i = 0; i < buttons.length; i++) {
      inputGroup.appendChild(buttons[i])
    }

    return inputGroupContainer
  }
})
<commit_msg>set id and for attributes for label/input click<commit_after>import { AbstractTheme } from '../theme'

export var bootstrap4Theme = AbstractTheme.extend({
  /* Theme config options that allows changing various aspects of the output */
  options: {
    disable_theme_rules: false,
    input_size: 'normal', // Size of input and select elements. "small", "normal", "large"
    custom_forms: false // use twbs custom form stylings
  },
  /* Custom stylesheet rules. format: "selector" : "CSS rules" */
  rules: {
    '.jsoneditor-twbs4-text-button': 'background: none;padding: 0;border: 0',
    'td>.form-group': 'margin-bottom: 0',
    'div[data-schemaid="root"]:after': 'position:relative;color:red;margin:10px 0;font-weight:600;display:block;width:100%;text-align:center;content:"This is an old JSON-Editor 1.x Theme and might not display elements correctly when used with the 2.x version"'
  },

  getSelectInput: function (options, multiple) {
    var el = this._super(options)
    el.classList.add('form-control')

    if (this.options.custom_forms === false) {
      if (this.options.input_size === 'small') el.classList.add('form-control-sm')
      if (this.options.input_size === 'large') el.classList.add('form-control-lg')
    } else {
      el.classList.remove('form-control')
      el.classList.add('custom-select')
      if (this.options.input_size === 'small') el.classList.add('custom-select-sm')
      if (this.options.input_size === 'large') el.classList.add('custom-select-lg')
    }

    return el
  },

  setGridColumnSize: function (el, size, offset) {
    el.classList.add('col-md-' + size)

    if (offset) {
      el.classList.add('offset-md-' + offset)
    }
  },

  afterInputReady: function (input) {
    if (input.controlgroup) return

    // set id/for
    var id = input.name
    input.id = id
    // 2x parentNode, b/c range input has an
 wrapper
    var label = input.parentNode.parentNode.getElementsByTagName('label')[0]
    if (label) {
      label.htmlFor = id
    }

    input.controlgroup = this.closest(input, '.form-group')
  },

  getTextareaInput: function () {
    var el = document.createElement('textarea')
    el.classList.add('form-control')
    if (this.options.input_size === 'small') el.classList.add('form-control-sm')
    if (this.options.input_size === 'large') el.classList.add('form-control-lg')
    return el
  },

  getRangeInput: function (min, max, step) {
    var el = this._super(min, max, step)

    if (this.options.custom_forms === true) {
      el.classList.remove('form-control')
      el.classList.add('custom-range')
    }

    return el
  },

  getFormInputField: function (type) {
    var el = this._super(type)
    if (type !== 'checkbox' && type !== 'radio') {
      el.classList.add('form-control')
      if (this.options.input_size === 'small') el.classList.add('form-control-sm')
      if (this.options.input_size === 'large') el.classList.add('form-control-lg')
    }
    return el
  },

  getFormControl: function (label, input, description, infoText) {
    var group = document.createElement('div')
    group.classList.add('form-group')

    if (label && (input.type === 'checkbox' || input.type === 'radio')) {
      var check = document.createElement('div')

      if (this.options.custom_forms === false) {
        check.classList.add('form-check')
        input.classList.add('form-check-input')
        label.classList.add('form-check-label')
      } else {
        check.classList.add('custom-control')
        input.classList.add('custom-control-input')
        label.classList.add('custom-control-label')

        if (input.type === 'checkbox') {
          check.classList.add('custom-checkbox')
        } else {
          check.classList.add('custom-radio')
        }
      }

      check.appendChild(input)
      check.appendChild(label)
      if (infoText) check.appendChild(infoText)

      group.appendChild(check)
    } else {
      if (label) {
        group.appendChild(label)

        if (infoText) group.appendChild(infoText)
      }

      group.appendChild(input)
    }

    if (description) {
      group.appendChild(description)
    }

    return group
  },

  getInfoButton: function (text) {
    var button = document.createElement('button') // shoud be a <button> but no fitting tbws style...
    button.type = 'button'
    button.classList.add('ml-3', 'jsoneditor-twbs4-text-button')
    button.setAttribute('data-toggle', 'tooltip')
    button.setAttribute('data-placement', 'auto')
    button.title = text

    var icon = document.createTextNode('ⓘ')
    button.appendChild(icon)

    var tooltip = document.createElement('div')
    tooltip.classList.add('tooltip', 'bs-tooltip-top')
    tooltip.setAttribute('role', 'tooltip')

    if (window.jQuery) {
      // arrow is only usefull if positioned by popover.js
      var arrow = document.createElement('div')
      arrow.classList.add('arrow')
      tooltip.appendChild(arrow)

      window.jQuery(button).tooltip()
    } else {
      // better fallback for non-js / no twbs js situtions?
      // for now just rely on browser native [title]
    }

    return button
  },

  /**
   * Generates a checkbox...
   *
   * Overwriten from master theme to get rid of inline styles.
   */
  getCheckbox: function () {
    var el = this.getFormInputField('checkbox')
    return el
  },

  /**
   * Multiple checkboxes in a row.
   *
   */
  getMultiCheckboxHolder: function (controls, label, description, infoText) {
    var el = document.createElement('div')
    el.classList.add('form-group')

    if (label) {
      el.appendChild(label)

      if (infoText) {
        label.appendChild(infoText)
      }
    }

    // for inline view we need an container so it doesnt wrap in the "row" of the
    var container = document.createElement('div')

    for (var i in controls) {
      if (!controls.hasOwnProperty(i)) {
        continue
      }

      // controls are already parsed by getFormControl() so they have an .form-group
      // wrapper we need to get rid of...
      var ctrl = controls[i].firstChild

      // we don't know if this should be an normal / compact view
      /* if (this.options.custom_forms === false) {
        ctrl.classList.add('form-check-inline')
      } else {
        ctrl.classList.add('custom-control-inline')
      } */

      container.appendChild(ctrl)
    }

    el.appendChild(container)

    if (description) el.appendChild(description)

    return el
  },

  /**
   * Single radio element
   */
  getFormRadio: function (attributes) {
    var el = this.getFormInputField('radio')

    for (var key in attributes) {
      el.setAttribute(key, attributes[key])
    }

    if (this.options.custom_forms === false) {
      el.classList.add('form-check-input')
    } else {
      el.classList.add('custom-control-input')
    }

    return el
  },

  /**
   * Add the  for the single radio from getFormRadio()
   *
   */
  getFormRadioLabel: function (text, req) {
    var el = document.createElement('label')

    if (this.options.custom_forms === false) {
      el.classList.add('form-check-label')
    } else {
      el.classList.add('custom-control-label')
    }

    el.appendChild(document.createTextNode(text))
    return el
  },

  /**
   * Stack the radios from getFormRadio()/getFormRadioLabel()
   *
   */
  getFormRadioControl: function (label, input, compact) {
    var el = document.createElement('div')

    if (this.options.custom_forms === false) {
      el.classList.add('form-check')
    } else {
      el.classList.add('custom-control', 'custom-radio')
    }

    el.appendChild(input)
    el.appendChild(label)

    if (compact) {
      if (this.options.custom_forms === false) {
        el.classList.add('form-check-inline')
      } else {
        el.classList.add('custom-control-inline')
      }
    }

    return el
  },

  getIndentedPanel: function () {
    var el = document.createElement('div')
    el.classList.add('card', 'card-body', 'bg-light', 'mb-3')

    // for better twbs card styling we should be able to return a nested div

    return el
  },

  getFormInputDescription: function (text) {
    var el = document.createElement('small')
    el.classList.add('form-text')

    if (window.DOMPurify) {
      el.innerHTML = window.DOMPurify.sanitize(text)
    } else {
      el.textContent = this.cleanText(text)
    }

    return el
  },

  getHeader: function (text) {
    // var cardHeader = document.createElement('div')
    // cardHeader.classList.add('card-header')

    var el = document.createElement('h3')
    el.classList.add('card-title')

    if (typeof text === 'string') {
      el.textContent = text
    } else {
      text.classList.add('pr-3')
      el.appendChild(text)
    }

    // cardHeader.appendChild(el)

    return el
  },
  getHeaderButtonHolder: function () {
    var el = this.getButtonHolder()

    return el
  },
  getButtonHolder: function () {
    // todo: we dont want to use div.btn-group until we can wrap all buttons inside it
    // single button inside a group get's it's corners cut off
    var el = document.createElement('span')
    // el.classList.add('btn-group')
    // el.classList.add('ml-2')
    return el
  },
  getButton: function (text, icon, title) {
    var el = this._super(text, icon, title)
    el.classList.add('btn', 'btn-secondary', 'btn-sm', 'mr-2')
    return el
  },

  getTable: function () {
    var el = document.createElement('table')
    el.classList.add('table', 'table-bordered', 'table-sm')
    return el
  },

  /**
   * input validation on <input>
   */
  addInputError: function (input, text) {
    if (!input.controlgroup) return

    input.classList.add('is-invalid')

    if (!input.errmsg) {
      input.errmsg = document.createElement('p')
      input.errmsg.classList.add('invalid-feedback')
      input.controlgroup.appendChild(input.errmsg)
    } else {
      input.errmsg.style.display = ''
    }

    input.errmsg.textContent = text
  },
  removeInputError: function (input) {
    if (!input.errmsg) return
    input.errmsg.style.display = 'none'
    input.classList.remove('is-invalid')
  },

  getTabHolder: function (propertyName) {
    var el = document.createElement('div')
    var pName = (typeof propertyName === 'undefined') ? '' : propertyName
    el.innerHTML = "

"
    el.classList.add('row')
    return el
  },
  addTab: function (holder, tab) {
    holder.children[0].children[0].appendChild(tab)
  },
  getTabContentHolder: function (tabHolder) {
    return tabHolder.children[1].children[0]
  },

  getTopTabHolder: function (propertyName) {
    var pName = (typeof propertyName === 'undefined') ? '' : propertyName

    var el = document.createElement('div')
    el.classList.add('card')
    el.innerHTML = "

"

    return el
  },
  getTab: function (text, tabId) {
    var liel = document.createElement('li')
    liel.classList.add('nav-item')

    var ael = document.createElement('a')
    ael.classList.add('nav-link')
    ael.setAttribute('href', '#' + tabId)
    ael.setAttribute('data-toggle', 'tab')
    ael.appendChild(text)

    liel.appendChild(ael)

    return liel
  },
  getTopTab: function (text, tabId) {
    var el = document.createElement('li')
    el.classList.add('nav-item')

    var a = document.createElement('a')
    a.classList.add('nav-link')
    a.setAttribute('href', '#' + tabId)
    a.setAttribute('data-toggle', 'tab')
    a.appendChild(text)

    el.appendChild(a)

    return el
  },
  getTabContent: function () {
    var el = document.createElement('div')
    el.classList.add('tab-pane')
    el.setAttribute('role', 'tabpanel')
    return el
  },
  getTopTabContent: function () {
    var el = document.createElement('div')
    el.classList.add('tab-pane')
    el.setAttribute('role', 'tabpanel')
    return el
  },
  markTabActive: function (row) {
    row.tab.firstChild.classList.add('active')

    if (typeof row.rowPane !== 'undefined') {
      row.rowPane.classList.add('active')
    } else {
      row.container.classList.add('active')
    }
  },
  markTabInactive: function (row) {
    row.tab.firstChild.classList.remove('active')

    if (typeof row.rowPane !== 'undefined') {
      row.rowPane.classList.remove('active')
    } else {
      row.container.classList.remove('active')
    }
  },

  addTopTab: function (holder, tab) {
    holder.children[0].children[0].appendChild(tab)
  },

  getTopTabContentHolder: function (tabHolder) {
    return tabHolder.children[1].children[0]
  },

  getProgressBar: function () {
    var min = 0
    var max = 100
    var start = 0

    var container = document.createElement('div')
    container.classList.add('progress')

    var bar = document.createElement('div')
    bar.classList.add('progress-bar')
    bar.setAttribute('role', 'progressbar')
    bar.setAttribute('aria-valuenow', start)
    bar.setAttribute('aria-valuemin', min)
    bar.setAttribute('aria-valuenax', max)
    bar.innerHTML = start + '%'
    container.appendChild(bar)

    return container
  },
  updateProgressBar: function (progressBar, progress) {
    if (!progressBar) return

    var bar = progressBar.firstChild
    var percentage = progress + '%'
    bar.setAttribute('aria-valuenow', progress)
    bar.style.width = percentage
    bar.innerHTML = percentage
  },
  updateProgressBarUnknown: function (progressBar) {
    if (!progressBar) return

    var bar = progressBar.firstChild
    progressBar.classList.add('progress', 'progress-striped', 'active')
    bar.removeAttribute('aria-valuenow')
    bar.style.width = '100%'
    bar.innerHTML = ''
  },
  getInputGroup: function (input, buttons) {
    if (!input) return

    var inputGroupContainer = document.createElement('div')
    inputGroupContainer.classList.add('input-group')
    inputGroupContainer.appendChild(input)

    var inputGroup = document.createElement('div')
    inputGroup.classList.add('input-group-prepend')
    inputGroupContainer.appendChild(inputGroup)

    for (var i = 0; i < buttons.length; i++) {
      inputGroup.appendChild(buttons[i])
    }

    return inputGroupContainer
  }
})
<|endoftext|>


<commit_before>// Base Foundation theme
JSONEditor.defaults.themes.foundation = JSONEditor.AbstractTheme.extend({
  getChildEditorHolder: function() {
    var el = document.createElement('div');
    el.style.marginBottom = '15px';
    return el;
  },
  getSelectInput: function(options) {
    var el = this._super(options);
    el.style.minWidth = 'none';
    el.style.padding = '5px';
    el.style.marginTop = '3px';
    return el;
  },
  getSwitcher: function(options) {
    var el = this._super(options);
    el.style.paddingRight = '8px';
    return el;
  },
  afterInputReady: function(input) {
    if(this.closest(input,'.compact')) {
      input.style.marginBottom = 0;
    }
    input.group = this.closest(input,'.form-control');
  },
  getFormInputLabel: function(text) {
    var el = this._super(text);
    el.style.display = 'inline-block';
    return el;
  },
  getFormInputField: function(type) {
    var el = this._super(type);
    el.style.width = '100%';
    el.style.marginBottom = type==='checkbox'? '0' : '12px';
    return el;
  },
  getFormInputDescription: function(text) {
    var el = document.createElement('p');
    el.textContent = text;
    el.style.marginTop = '-10px';
    el.style.fontStyle = 'italic';
    return el;
  },
  getIndentedPanel: function() {
    var el = document.createElement('div');
    el.className = 'panel';
    return el;
  },
  getHeaderButtonHolder: function() {
    var el = this.getButtonHolder();
    el.style.display = 'inline-block';
    el.style.marginLeft = '10px';
    el.style.verticalAlign = 'middle';
    return el;
  },
  getButtonHolder: function() {
    var el = document.createElement('div');
    el.className = 'button-group';
    return el;
  },
  getButton: function(text, icon, title) {
    var el = this._super(text, icon, title);
    el.className += ' small button';
    return el;
  },
  addInputError: function(input,text) {
    if(!input.group) return;
    input.group.className += ' error';

    if(!input.errmsg) {
      input.insertAdjacentHTML('afterend','');
      input.errmsg = input.parentNode.getElementsByClassName('errormsg')[0];
    }
    else {
      input.errmsg.style.display = '';
    }

    input.errmsg.textContent = text;
  },
  removeInputError: function(input) {
    if(!input.errmsg) return;
    input.group.className = input.group.className.replace(/ error/g,'');
    input.errmsg.style.display = 'none';
  },
  getProgressBar: function() {
    var progressBar = document.createElement('div');
    progressBar.className = 'progress';

    var meter = document.createElement('span');
    meter.className = 'meter';
    meter.style.width = '0%';
    progressBar.appendChild(meter);
    return progressBar;
  },
  updateProgressBar: function(progressBar, progress) {
    if (!progressBar) return;
    progressBar.firstChild.style.width = progress + '%';
  },
  updateProgressBarUnknown: function(progressBar) {
    if (!progressBar) return;
    progressBar.firstChild.style.width = '100%';
  }
});

// Foundation 3 Specific Theme
JSONEditor.defaults.themes.foundation3 = JSONEditor.defaults.themes.foundation.extend({
  getHeaderButtonHolder: function() {
    var el = this._super();
    el.style.fontSize = '.6em';
    return el;
  },
  getFormInputLabel: function(text) {
    var el = this._super(text);
    el.style.fontWeight = 'bold';
    return el;
  },
  getTabHolder: function() {
    var el = document.createElement('div');
    el.className = 'row';
    el.innerHTML = "

";
    return el;
  },
  setGridColumnSize: function(el,size) {
    var sizes = ['zero','one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve'];
    el.className = 'columns '+sizes[size];
  },
  getTab: function(text) {
    var el = document.createElement('dd');
    var a = document.createElement('a');
    a.setAttribute('href','#');
    a.appendChild(text);
    el.appendChild(a);
    return el;
  },
  getTabContentHolder: function(tab_holder) {
    return tab_holder.children[1];
  },
  getTabContent: function() {
    var el = document.createElement('div');
    el.className = 'content active';
    el.style.paddingLeft = '5px';
    return el;
  },
  markTabActive: function(tab) {
    tab.className += ' active';
  },
  markTabInactive: function(tab) {
    tab.className = tab.className.replace(/\s*active/g,'');
  },
  addTab: function(holder, tab) {
    holder.children[0].appendChild(tab);
  }
});

// Foundation 4 Specific Theme
JSONEditor.defaults.themes.foundation4 = JSONEditor.defaults.themes.foundation.extend({
  getHeaderButtonHolder: function() {
    var el = this._super();
    el.style.fontSize = '.6em';
    return el;
  },
  setGridColumnSize: function(el,size) {
    el.className = 'columns large-'+size;
  },
  getFormInputDescription: function(text) {
    var el = this._super(text);
    el.style.fontSize = '.8rem';
    return el;
  },
  getFormInputLabel: function(text) {
    var el = this._super(text);
    el.style.fontWeight = 'bold';
    return el;
  }
});

// Foundation 5 Specific Theme
JSONEditor.defaults.themes.foundation5 = JSONEditor.defaults.themes.foundation.extend({
  getFormInputDescription: function(text) {
    var el = this._super(text);
    el.style.fontSize = '.8rem';
    return el;
  },
  setGridColumnSize: function(el,size) {
    el.className = 'columns medium-'+size;
  },
  getButton: function(text, icon, title) {
    var el = this._super(text,icon,title);
    el.className = el.className.replace(/\s*small/g,'') + ' tiny';
    return el;
  },
  getTabHolder: function() {
    var el = document.createElement('div');
    el.innerHTML = "

";
    return el;
  },
  getTab: function(text) {
    var el = document.createElement('dd');
    var a = document.createElement('a');
    a.setAttribute('href','#');
    a.appendChild(text);
    el.appendChild(a);
    return el;
  },
  getTabContentHolder: function(tab_holder) {
    return tab_holder.children[1];
  },
  getTabContent: function() {
    var el = document.createElement('div');
    el.className = 'content active';
    el.style.paddingLeft = '5px';
    return el;
  },
  markTabActive: function(tab) {
    tab.className += ' active';
  },
  markTabInactive: function(tab) {
    tab.className = tab.className.replace(/\s*active/g,'');
  },
  addTab: function(holder, tab) {
    holder.children[0].appendChild(tab);
  }
});
<commit_msg>use correct css class for latest foundation<commit_after>// Base Foundation theme
JSONEditor.defaults.themes.foundation = JSONEditor.AbstractTheme.extend({
  getChildEditorHolder: function() {
    var el = document.createElement('div');
    el.style.marginBottom = '15px';
    return el;
  },
  getSelectInput: function(options) {
    var el = this._super(options);
    el.style.minWidth = 'none';
    el.style.padding = '5px';
    el.style.marginTop = '3px';
    return el;
  },
  getSwitcher: function(options) {
    var el = this._super(options);
    el.style.paddingRight = '8px';
    return el;
  },
  afterInputReady: function(input) {
    if(this.closest(input,'.compact')) {
      input.style.marginBottom = 0;
    }
    input.group = this.closest(input,'.form-control');
  },
  getFormInputLabel: function(text) {
    var el = this._super(text);
    el.style.display = 'inline-block';
    return el;
  },
  getFormInputField: function(type) {
    var el = this._super(type);
    el.style.width = '100%';
    el.style.marginBottom = type==='checkbox'? '0' : '12px';
    return el;
  },
  getFormInputDescription: function(text) {
    var el = document.createElement('p');
    el.textContent = text;
    el.style.marginTop = '-10px';
    el.style.fontStyle = 'italic';
    return el;
  },
  getIndentedPanel: function() {
    var el = document.createElement('div');
    el.className = 'panel';
    return el;
  },
  getHeaderButtonHolder: function() {
    var el = this.getButtonHolder();
    el.style.display = 'inline-block';
    el.style.marginLeft = '10px';
    el.style.verticalAlign = 'middle';
    return el;
  },
  getButtonHolder: function() {
    var el = document.createElement('div');
    el.className = 'button-group';
    return el;
  },
  getButton: function(text, icon, title) {
    var el = this._super(text, icon, title);
    el.className += ' small button';
    return el;
  },
  addInputError: function(input,text) {
    if(!input.group) return;
    input.group.className += ' error';

    if(!input.errmsg) {
      input.insertAdjacentHTML('afterend','');
      input.errmsg = input.parentNode.getElementsByClassName('error')[0];
    }
    else {
      input.errmsg.style.display = '';
    }

    input.errmsg.textContent = text;
  },
  removeInputError: function(input) {
    if(!input.errmsg) return;
    input.group.className = input.group.className.replace(/ error/g,'');
    input.errmsg.style.display = 'none';
  },
  getProgressBar: function() {
    var progressBar = document.createElement('div');
    progressBar.className = 'progress';

    var meter = document.createElement('span');
    meter.className = 'meter';
    meter.style.width = '0%';
    progressBar.appendChild(meter);
    return progressBar;
  },
  updateProgressBar: function(progressBar, progress) {
    if (!progressBar) return;
    progressBar.firstChild.style.width = progress + '%';
  },
  updateProgressBarUnknown: function(progressBar) {
    if (!progressBar) return;
    progressBar.firstChild.style.width = '100%';
  }
});

// Foundation 3 Specific Theme
JSONEditor.defaults.themes.foundation3 = JSONEditor.defaults.themes.foundation.extend({
  getHeaderButtonHolder: function() {
    var el = this._super();
    el.style.fontSize = '.6em';
    return el;
  },
  getFormInputLabel: function(text) {
    var el = this._super(text);
    el.style.fontWeight = 'bold';
    return el;
  },
  getTabHolder: function() {
    var el = document.createElement('div');
    el.className = 'row';
    el.innerHTML = "

";
    return el;
  },
  setGridColumnSize: function(el,size) {
    var sizes = ['zero','one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve'];
    el.className = 'columns '+sizes[size];
  },
  getTab: function(text) {
    var el = document.createElement('dd');
    var a = document.createElement('a');
    a.setAttribute('href','#');
    a.appendChild(text);
    el.appendChild(a);
    return el;
  },
  getTabContentHolder: function(tab_holder) {
    return tab_holder.children[1];
  },
  getTabContent: function() {
    var el = document.createElement('div');
    el.className = 'content active';
    el.style.paddingLeft = '5px';
    return el;
  },
  markTabActive: function(tab) {
    tab.className += ' active';
  },
  markTabInactive: function(tab) {
    tab.className = tab.className.replace(/\s*active/g,'');
  },
  addTab: function(holder, tab) {
    holder.children[0].appendChild(tab);
  }
});

// Foundation 4 Specific Theme
JSONEditor.defaults.themes.foundation4 = JSONEditor.defaults.themes.foundation.extend({
  getHeaderButtonHolder: function() {
    var el = this._super();
    el.style.fontSize = '.6em';
    return el;
  },
  setGridColumnSize: function(el,size) {
    el.className = 'columns large-'+size;
  },
  getFormInputDescription: function(text) {
    var el = this._super(text);
    el.style.fontSize = '.8rem';
    return el;
  },
  getFormInputLabel: function(text) {
    var el = this._super(text);
    el.style.fontWeight = 'bold';
    return el;
  }
});

// Foundation 5 Specific Theme
JSONEditor.defaults.themes.foundation5 = JSONEditor.defaults.themes.foundation.extend({
  getFormInputDescription: function(text) {
    var el = this._super(text);
    el.style.fontSize = '.8rem';
    return el;
  },
  setGridColumnSize: function(el,size) {
    el.className = 'columns medium-'+size;
  },
  getButton: function(text, icon, title) {
    var el = this._super(text,icon,title);
    el.className = el.className.replace(/\s*small/g,'') + ' tiny';
    return el;
  },
  getTabHolder: function() {
    var el = document.createElement('div');
    el.innerHTML = "

";
    return el;
  },
  getTab: function(text) {
    var el = document.createElement('dd');
    var a = document.createElement('a');
    a.setAttribute('href','#');
    a.appendChild(text);
    el.appendChild(a);
    return el;
  },
  getTabContentHolder: function(tab_holder) {
    return tab_holder.children[1];
  },
  getTabContent: function() {
    var el = document.createElement('div');
    el.className = 'content active';
    el.style.paddingLeft = '5px';
    return el;
  },
  markTabActive: function(tab) {
    tab.className += ' active';
  },
  markTabInactive: function(tab) {
    tab.className = tab.className.replace(/\s*active/g,'');
  },
  addTab: function(holder, tab) {
    holder.children[0].appendChild(tab);
  }
});
<|endoftext|>


<commit_before>import dining from './dining/dining';
import notify from './notify';
import reservations from './config/reservations';
import schedule from 'node-schedule';

var processReservations = function(element, index, array) {
  dining(element).then(notify);
};

// cron style, run every 15 minutes
schedule.scheduleJob('*/15 * * * *', function() {
  reservations.forEach(processReservations);
});
<commit_msg>eslint stuff<commit_after>import dining from './dining/dining';
import notify from './notify';
import reservations from './config/reservations';
import schedule from 'node-schedule';

const processReservations = (element) => {
  dining(element).then(notify);
};

// cron style, run every 15 minutes
schedule.scheduleJob('*/15 * * * *', () => {
  reservations.forEach(processReservations);
});
<|endoftext|>


<commit_before>// Might need refactoring
describe.only('Popup component', function() {
  var grid, titleText = 'title';

    describe('initialization', function() {
        it('should be defined', function() {
            expect(grid).to.be.ok();
        });

        it('should be found in DOM', function() {
            var $oskariGridTable = jQuery('.oskari-grid');
            expect($oskariGrid.length).to.equal(1);
        });
        it('should have a column selector in DOM', function() {
            var $oskariGridSelector = jQuery('.column-selector');
            expect($oskariGridSelector.length).to.equal(1);
        });
        it('should have a name visible in DOM', function() {
            var $name = jQuery('div.oskarifield').find('input').attr('name');
            expect($name).to.be(testName);
        });

    });

/*
                // if multiple featuredatas, we will have a "__featureName" field in "all" model -> rename it for ui
                grid.setColumnUIName('__featureName', this.instance.getLocalization('featureNameAll'));
                // set selection handler
                grid.addSelectionListener(function(pGrid, dataId) {
                    me._handleGridSelect(layer, dataId);
                });

                // set popup handler for inner data
                var showMore = this.instance.getLocalization('showmore');
                grid.setAdditionalDataHandler(showMore,
                    function(link, content) {
                        var dialog = Oskari.clazz.create('Oskari.userinterface.component.Popup');
                        var okBtn = dialog.createCloseButton("OK");
                        okBtn.addClass('primary');
                        dialog.show(showMore, content, [okBtn]);
                        dialog.moveTo(link, 'bottom');
                });

                var visibleFields = [];
                // filter out certain fields
                for(var i = 0; i < fields.length; ++i) {
                    if(fields[i] != 'featureId' &&
                       fields[i] != 'qName') {
                           visibleFields.push(fields[i]);
                    }
                }
                grid.setVisibleFields(visibleFields);
                grid.setColumnSelector(true);
                grid.setResizableColumns(true);
*/

/*  it('should be found in DOM', function(){
    var popup = jQuery('div.divmanazerpopup');
    expect(popup.length).to.equal(1);
  });

  it('should have a title', function() {
    var popup = jQuery('div.divmanazerpopup');
    var title = popup.find('h3').html();
    expect(title).not.to.be(null);
    expect(title).to.equal('title'); // dialog.getTitle()
  });
*/

  beforeEach(function() {
debugger;
    var data = [
        {'row0': 0, 'row1':1,'row2':2,'row3':3},
        {'row0': 00, 'row1':11,'row2':22,'row3':33},
        {'row0': 000, 'row1':111,'row2':222,'row3':333},
        {'row0': 0000, 'row1':1111,'row2':2222,'row3':3333},
        {'row0': 00000, 'row1':11111,'row2':22222,'row3':33333}
    ];

    var gridModel = Oskari.clazz.create('Oskari.userinterface.component.GridModel');
    gridModel.addData(data[0]);
    gridModel.addData(data[1]);
    gridModel.addData(data[2]);
    gridModel.addData(data[3]);
    gridModel.addData(data[4]);

    grid = Oskari.clazz.create('Oskari.userinterface.component.Grid', 'Grid test');

    grid.setDataModel(gridModel);
    grid.setColumnUIName('__featureName', 'featureNameAll');
    var visibleFields = ['row1','row2','row3'];
    grid.setVisibleFields(visibleFields);
    grid.setColumnSelector(true);
    grid.setResizableColumns(true);

    grid.renderTo(jQuery('body'));
  });

  afterEach(function() {
        jQuery('body').children().remove();
    });
});<commit_msg>some tests<commit_after>// Might need refactoring
describe.only('Grid component', function() {
  var grid, titleText = 'title';

    describe('initialization', function() {
        it('should be defined', function() {
            expect(grid).to.be.ok();
        });

        it('should be found in DOM', function() {
            var $oskariGridTable = jQuery('.oskari-grid');
            expect($oskariGridTable.length).to.equal(1);
        });
        it('should have a column selector in DOM', function() {
            var $oskariGridSelector = jQuery('.column-selector');
            expect($oskariGridSelector.length).to.equal(1);
        });
    });

  beforeEach(function() {
    var data = [
        {'row0': 0, 'row1':1,'row2':2,'row3':3},
        {'row0': 00, 'row1':11,'row2':22,'row3':33},
        {'row0': 000, 'row1':111,'row2':222,'row3':333},
        {'row0': 0000, 'row1':1111,'row2':2222,'row3':3333},
        {'row0': 00000, 'row1':11111,'row2':22222,'row3':33333}
    ];

    var gridModel = Oskari.clazz.create('Oskari.userinterface.component.GridModel');
    gridModel.addData(data[0]);
    gridModel.addData(data[1]);
    gridModel.addData(data[2]);
    gridModel.addData(data[3]);
    gridModel.addData(data[4]);

    grid = Oskari.clazz.create('Oskari.userinterface.component.Grid', 'Grid test');

    grid.setDataModel(gridModel);
    grid.setColumnUIName('__featureName', 'featureNameAll');
    var visibleFields = ['row1','row2','row3'];
    grid.setVisibleFields(visibleFields);
    grid.setColumnSelector(true);
    grid.setResizableColumns(true);

    grid.renderTo(jQuery('body'));
  });

  afterEach(function() {
        jQuery('body').children().remove();
    });

});<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// 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(shared, testing) {

  var shorthandToLonghand = {
    background: [
      'background-image',
      'background-position',
      'background-size',
      'background-repeat',
      'background-attachment',
      'background-origin',
      'background-clip',
      'background-color'
    ],
    border: [
      'border-top-color',
      'border-top-style',
      'border-top-width',
      'border-right-color',
      'border-right-style',
      'border-right-width',
      'border-bottom-color',
      'border-bottom-style',
      'border-bottom-width',
      'border-left-color',
      'border-left-style',
      'border-left-width'
    ],
    borderBottom: [
      'border-bottom-width',
      'border-bottom-style',
      'border-bottom-color'
    ],
    borderColor: [
      'border-top-color',
      'border-right-color',
      'border-bottom-color',
      'border-left-color'
    ],
    borderLeft: [
      'border-left-width',
      'border-left-style',
      'border-left-color'
    ],
    borderRadius: [
      'border-top-left-radius',
      'border-top-right-radius',
      'border-bottom-right-radius',
      'border-bottom-left-radius'
    ],
    borderRight: [
      'border-right-width',
      'border-right-style',
      'border-right-color'
    ],
    borderTop: [
      'border-top-width',
      'border-top-style',
      'border-top-color'
    ],
    borderWidth: [
      'border-top-width',
      'border-right-width',
      'border-bottom-width',
      'border-left-width'
    ],
    flex: [
      'flex-grow',
      'flex-shrink',
      'flex-basis'
    ],
    font: [
      'font-family',
      'font-size',
      'font-style',
      'font-variant',
      'font-weight',
      'line-height'
    ],
    margin: [
      'margin-top',
      'margin-right',
      'margin-bottom',
      'margin-left'
    ],
    outline: [
      'outline-color',
      'outline-style',
      'outline-width'
    ],
    padding: [
      'padding-top',
      'padding-right',
      'padding-bottom',
      'padding-left'
    ]
  };

  var shorthandExpanderElem = document.createElement('div');

  // This delegates parsing shorthand value syntax to the browser.
  function expandShorthand(property, value, result) {
    shorthandExpanderElem.style[property] = value;
    var longProperties = shorthandToLonghand[property];
    if (longProperties) {
      for (var i in longProperties) {
        var longProperty = longProperties[i];
        var longhandValue = shorthandExpanderElem.style[longProperty];
        result[longProperty] = longhandValue;
      }
    } else {
      result[property] = value;
    }
  };

  function normalizeKeyframes(effectInput) {
    if (!Array.isArray(effectInput) && effectInput !== null)
      throw new TypeError('Keyframe effect must be null or an array of keyframes');

    if (effectInput == null)
      return [];

    var keyframeEffect = effectInput.map(function(originalKeyframe) {
      var keyframe = {};
      for (var member in originalKeyframe) {
        var memberValue = originalKeyframe[member];
        if (member == 'offset') {
          if (memberValue != null) {
            memberValue = Number(memberValue);
            if (!isFinite(memberValue))
              throw new TypeError('keyframe offsets must be numbers.');
          }
        } else if (member == 'composite') {
          throw {
            type: DOMException.NOT_SUPPORTED_ERR,
            name: 'NotSupportedError',
            message: 'add compositing is not supported'
          };
        } else if (member == 'easing') {
          memberValue = shared.toTimingFunction(memberValue);
        } else {
          memberValue = '' + memberValue;
        }
        expandShorthand(member, memberValue, keyframe);
      }
      if (keyframe.offset == undefined)
        keyframe.offset = null;
      if (keyframe.easing == undefined)
        keyframe.easing = shared.toTimingFunction('linear');
      return keyframe;
    });

    var everyFrameHasOffset = true;
    var looselySortedByOffset = true;
    var previousOffset = -Infinity;
    for (var i = 0; i < keyframeEffect.length; i++) {
      var offset = keyframeEffect[i].offset;
      if (offset != null) {
        if (offset < previousOffset) {
          throw {
            code: DOMException.INVALID_MODIFICATION_ERR,
            name: 'InvalidModificationError',
            message: 'Keyframes are not loosely sorted by offset. Sort or specify offsets.'
          };
        }
        previousOffset = offset;
      } else {
        everyFrameHasOffset = false;
      }
    }

    keyframeEffect = keyframeEffect.filter(function(keyframe) {
      return keyframe.offset >= 0 && keyframe.offset <= 1;
    });

    function spaceKeyframes() {
      var length = keyframeEffect.length;
      if (keyframeEffect[length - 1].offset == null)
        keyframeEffect[length - 1].offset = 1;
      if (length > 1 && keyframeEffect[0].offset == null)
        keyframeEffect[0].offset = 0;

      var previousIndex = 0;
      var previousOffset = keyframeEffect[0].offset;
      for (var i = 1; i < length; i++) {
        var offset = keyframeEffect[i].offset;
        if (offset != null) {
          for (var j = 1; j < i - previousIndex; j++)
            keyframeEffect[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex);
          previousIndex = i;
          previousOffset = offset;
        }
      }
    }
    if (!everyFrameHasOffset)
      spaceKeyframes();

    return keyframeEffect;
  }

  shared.normalizeKeyframes = normalizeKeyframes;

  if (WEB_ANIMATIONS_TESTING) {
    testing.normalizeKeyframes = normalizeKeyframes;
  }

})(webAnimationsShared, webAnimationsTesting);
<commit_msg>Change shorthand expansions back to camelCase<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// 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(shared, testing) {
  var shorthandToLonghand = {
    background: [
      'backgroundImage',
      'backgroundPosition',
      'backgroundSize',
      'backgroundRepeat',
      'backgroundAttachment',
      'backgroundOrigin',
      'backgroundClip',
      'backgroundColor'
    ],
    border: [
      'borderTopColor',
      'borderTopStyle',
      'borderTopWidth',
      'borderRightColor',
      'borderRightStyle',
      'borderRightWidth',
      'borderBottomColor',
      'borderBottomStyle',
      'borderBottomWidth',
      'borderLeftColor',
      'borderLeftStyle',
      'borderLeftWidth'
    ],
    borderBottom: [
      'borderBottomWidth',
      'borderBottomStyle',
      'borderBottomColor'
    ],
    borderColor: [
      'borderTopColor',
      'borderRightColor',
      'borderBottomColor',
      'borderLeftColor'
    ],
    borderLeft: [
      'borderLeftWidth',
      'borderLeftStyle',
      'borderLeftColor'
    ],
    borderRadius: [
      'borderTopLeftRadius',
      'borderTopRightRadius',
      'borderBottomRightRadius',
      'borderBottomLeftRadius'
    ],
    borderRight: [
      'borderRightWidth',
      'borderRightStyle',
      'borderRightColor'
    ],
    borderTop: [
      'borderTopWidth',
      'borderTopStyle',
      'borderTopColor'
    ],
    borderWidth: [
      'borderTopWidth',
      'borderRightWidth',
      'borderBottomWidth',
      'borderLeftWidth'
    ],
    flex: [
      'flexGrow',
      'flexShrink',
      'flexBasis'
    ],
    font: [
      'fontFamily',
      'fontSize',
      'fontStyle',
      'fontVariant',
      'fontWeight',
      'lineHeight'
    ],
    margin: [
      'marginTop',
      'marginRight',
      'marginBottom',
      'marginLeft'
    ],
    outline: [
      'outlineColor',
      'outlineStyle',
      'outlineWidth'
    ],
    padding: [
      'paddingTop',
      'paddingRight',
      'paddingBottom',
      'paddingLeft'
    ]
  };

  var shorthandExpanderElem = document.createElement('div');

  // This delegates parsing shorthand value syntax to the browser.
  function expandShorthand(property, value, result) {
    shorthandExpanderElem.style[property] = value;
    var longProperties = shorthandToLonghand[property];
    if (longProperties) {
      for (var i in longProperties) {
        var longProperty = longProperties[i];
        var longhandValue = shorthandExpanderElem.style[longProperty];
        result[longProperty] = longhandValue;
      }
    } else {
      result[property] = value;
    }
  };

  function normalizeKeyframes(effectInput) {
    if (!Array.isArray(effectInput) && effectInput !== null)
      throw new TypeError('Keyframe effect must be null or an array of keyframes');

    if (effectInput == null)
      return [];

    var keyframeEffect = effectInput.map(function(originalKeyframe) {
      var keyframe = {};
      for (var member in originalKeyframe) {
        var memberValue = originalKeyframe[member];
        if (member == 'offset') {
          if (memberValue != null) {
            memberValue = Number(memberValue);
            if (!isFinite(memberValue))
              throw new TypeError('keyframe offsets must be numbers.');
          }
        } else if (member == 'composite') {
          throw {
            type: DOMException.NOT_SUPPORTED_ERR,
            name: 'NotSupportedError',
            message: 'add compositing is not supported'
          };
        } else if (member == 'easing') {
          memberValue = shared.toTimingFunction(memberValue);
        } else {
          memberValue = '' + memberValue;
        }
        expandShorthand(member, memberValue, keyframe);
      }
      if (keyframe.offset == undefined)
        keyframe.offset = null;
      if (keyframe.easing == undefined)
        keyframe.easing = shared.toTimingFunction('linear');
      return keyframe;
    });

    var everyFrameHasOffset = true;
    var looselySortedByOffset = true;
    var previousOffset = -Infinity;
    for (var i = 0; i < keyframeEffect.length; i++) {
      var offset = keyframeEffect[i].offset;
      if (offset != null) {
        if (offset < previousOffset) {
          throw {
            code: DOMException.INVALID_MODIFICATION_ERR,
            name: 'InvalidModificationError',
            message: 'Keyframes are not loosely sorted by offset. Sort or specify offsets.'
          };
        }
        previousOffset = offset;
      } else {
        everyFrameHasOffset = false;
      }
    }

    keyframeEffect = keyframeEffect.filter(function(keyframe) {
      return keyframe.offset >= 0 && keyframe.offset <= 1;
    });

    function spaceKeyframes() {
      var length = keyframeEffect.length;
      if (keyframeEffect[length - 1].offset == null)
        keyframeEffect[length - 1].offset = 1;
      if (length > 1 && keyframeEffect[0].offset == null)
        keyframeEffect[0].offset = 0;

      var previousIndex = 0;
      var previousOffset = keyframeEffect[0].offset;
      for (var i = 1; i < length; i++) {
        var offset = keyframeEffect[i].offset;
        if (offset != null) {
          for (var j = 1; j < i - previousIndex; j++)
            keyframeEffect[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex);
          previousIndex = i;
          previousOffset = offset;
        }
      }
    }
    if (!everyFrameHasOffset)
      spaceKeyframes();

    return keyframeEffect;
  }

  shared.normalizeKeyframes = normalizeKeyframes;

  if (WEB_ANIMATIONS_TESTING) {
    testing.normalizeKeyframes = normalizeKeyframes;
  }

})(webAnimationsShared, webAnimationsTesting);
<|endoftext|>


<commit_before>var Trie = require('../lib/schema/trie/trie');
var Util = require('../lib/util');
var assert = require('assert');

var fakeDB = {
  db: {},
  put: function (key, value) {
    this.db[key] = value;
  },
  get: function (key) {
    if (this.db[key]) {
      return {
        data: this.db[key]
      };
    } else {
      return {
        error: 'value associated with key: ' + key + ' doesnt exist'
      };
    }
  },
  del: function (key) {
    delete this.db[key];
  },
  reset: function () {
    this.db = {};
  },
  isEmpty: function () {
    return Util.isEmpty(this.db)
  },
  size: function () {
    return Util.size(this.db);
  }
};
const LONG_VALUE = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ";

describe('Type checks:', function () {
  it('should type check trie.', function () {
    var n = new Trie(fakeDB, '', '');
    assert(Util.isTrie(n));
  });
});

describe('Should update and get:', function () {
  afterEach(fakeDB.reset);

  it('should update the trie with value provided.', function () {
    var trie = new Trie(fakeDB, '', '');
    trie.update('dog', LONG_VALUE);
    var result = trie.get('dog');
    assert(Util.deepEqual(result, LONG_VALUE));
  });

  // it('should do replace when update with same key', function () {
  //   var trie = new Trie(fakeDB, '', '');
  //   trie.update('dog', LONG_VALUE);
  //   var result = trie.get('dog');
  //   console.log('get dog results:' + result);
  //   trie.update('dog', LONG_VALUE + 'yolo');
  //   result = trie.get('dog');
  //   console.log('get dog results:' + result);
  //   assert(Util.deepEqual(result == LONG_VALUE));
  // });
});

// describe('should sync:', function () {
//   afterEach(fakeDB.reset);
//   var trie = new Trie(fakeDB, '', '');
//   it('should sync and update the db accordingly.', function () {
//     trie.update("dog", LONG_VALUE);
//     assert(fakeDB.isEmpty());
//     //sync
//     trie.cache.commit();
//     assert(!fakeDB.isEmpty());
//   });
// });

// describe('should undo:', function () {
//   afterEach(fakeDB.reset);
//   var trie = new Trie(fakeDB, '', '');
//   it('should undo and update the db accordingly.', function () {
//     trie.update("dog", LONG_VALUE);
//     trie.cache.commit();
//     var size = fakeDB.size();
//     //adding something else
//     trie.update("test", LONG_VALUE);
//     trie.cache.undo();
//     assert(size == fakeDB.size());
//   });
// });

// describe('should cache nodes:', function () {
//   after(fakeDB.reset);
//   var trie = new Trie(fakeDB, '', '');
//   it('cache nodes size should increase when we update trie.', function () {
//     trie.update("dog", LONG_VALUE);
//     var size = Util.size(trie.cache.nodes);
//     assert(size > 0);
//   });
//   it('cache nodes size should stay the same when we undo cache', function () {
//     trie.cache.undo();
//     var size = Util.size(trie.cache.nodes);
//     assert(size === 0);
//   });
// });<commit_msg>adding back trie tests<commit_after>var Trie = require('../lib/schema/trie/trie');
var Util = require('../lib/util');
var assert = require('assert');

var fakeDB = {
  db: {},
  put: function (key, value) {
    this.db[key] = value;
  },
  get: function (key) {
    if (this.db[key]) {
      return {
        data: this.db[key]
      };
    } else {
      return {
        error: 'value associated with key: ' + key + ' doesnt exist'
      };
    }
  },
  del: function (key) {
    delete this.db[key];
  },
  reset: function () {
    this.db = {};
  },
  isEmpty: function () {
    return Util.isEmpty(this.db)
  },
  size: function () {
    return Util.size(this.db);
  }
};
const LONG_VALUE = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ";

describe('Type checks:', function () {
  it('should type check trie.', function () {
    var n = new Trie(fakeDB, '', '');
    assert(Util.isTrie(n));
  });
});

describe('Should update and get:', function () {
  afterEach(fakeDB.reset);

  it('should update the trie with value provided.', function () {
    var trie = new Trie(fakeDB, '', '');
    trie.update('dog', LONG_VALUE);
    var result = trie.get('dog');
    assert(Util.deepEqual(result, LONG_VALUE));
  });

  it('should do replace when update with same key', function () {
    var trie = new Trie(fakeDB, '', '');
    trie.update('dog', LONG_VALUE);
    var result = trie.get('dog');
    trie.update('dog', LONG_VALUE + 'yolo');
    result = trie.get('dog');
    assert(Util.deepEqual(result, LONG_VALUE + 'yolo'));
  });
});

describe('should sync:', function () {
  afterEach(fakeDB.reset);
  var trie = new Trie(fakeDB, '', '');
  it('should sync and update the db accordingly.', function () {
    trie.update("dog", LONG_VALUE);
    assert(fakeDB.isEmpty());
    //sync
    trie.cache.commit();
    assert(!fakeDB.isEmpty());
  });
});

describe('should undo:', function () {
  afterEach(fakeDB.reset);
  var trie = new Trie(fakeDB, '', '');
  it('should undo and update the db accordingly.', function () {
    trie.update("dog", LONG_VALUE);
    trie.cache.commit();
    var size = fakeDB.size();
    //adding something else
    trie.update("test", LONG_VALUE);
    trie.cache.undo();
    assert(size == fakeDB.size());
  });
});

describe('should cache nodes:', function () {
  after(fakeDB.reset);
  var trie = new Trie(fakeDB, '', '');
  it('cache nodes size should increase when we update trie.', function () {
    trie.update("dog", LONG_VALUE);
    var size = Util.size(trie.cache.nodes);
    assert(size > 0);
  });
  it('cache nodes size should stay the same when we undo cache', function () {
    trie.cache.undo();
    var size = Util.size(trie.cache.nodes);
    assert(size === 0);
  });
});<|endoftext|>


<commit_before>/* jshint globalstrict:false, strict:false, unused : false */
/* global fail, assertTrue, assertFalse, assertEqual, TRANSACTION */
// //////////////////////////////////////////////////////////////////////////////
// / @brief tests for transactions
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2010-2012 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Jan Steemann
// / @author Copyright 2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////

var db = require('@arangodb').db;
var internal = require('internal');
var FoxxService = require('@arangodb/foxx/service');
var jsunity = require('jsunity');
var fs = require('fs');

function runSetup () {
  'use strict';
  internal.debugClearFailAt();

  let appPath = fs.join(FoxxService._appPath, '..');

  try {
    db._dropDatabase('UnitTestsRecovery1');
  } catch (err) {}

  db._createDatabase('UnitTestsRecovery1');

  fs.write(fs.join(appPath, 'UnitTestsRecovery1', 'foo.json'), 'test');

  try {
    db._dropDatabase('UnitTestsRecovery2');
  } catch (err) {}

  db._createDatabase('UnitTestsRecovery2');

  fs.write(fs.join(appPath, 'UnitTestsRecovery2', 'bar.json'), 'test');

  db._dropDatabase('UnitTestsRecovery2');

  // we need to wait long enough for the DatabaseManagerThread to
  // physically carry out the deletion
  let path = fs.join(appPath, 'UnitTestsRecovery2');
  let tries = 0;
  while (++tries < 10) {
    if (!fs.isDirectory(path)) {
      break;
    }
    internal.wait(1, false);
  }
  internal.debugTerminate('crashing server');
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief test suite
// //////////////////////////////////////////////////////////////////////////////

function recoverySuite () {
  'use strict';
  jsunity.jsUnity.attachAssertions();

  return {
    setUp: function () {},
    tearDown: function () {},

    // //////////////////////////////////////////////////////////////////////////////
    // / @brief test whether the data are correct after restart
    // //////////////////////////////////////////////////////////////////////////////

    testFoxxDirectories: function () {
      var appPath = fs.join(FoxxService._appPath, '..');

      assertTrue(fs.isDirectory(fs.join(appPath, 'UnitTestsRecovery1')));
      assertTrue(fs.isFile(fs.join(appPath, 'UnitTestsRecovery1', 'foo.json')));

      assertFalse(fs.isDirectory(fs.join(appPath, 'UnitTestsRecovery2')));
      assertFalse(fs.isFile(fs.join(appPath, 'UnitTestsRecovery2', 'bar.json')));
    }

  };
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief executes the test suite
// //////////////////////////////////////////////////////////////////////////////

function main (argv) {
  'use strict';
  if (argv[1] === 'setup') {
    runSetup();
    return 0;
  } else {
    jsunity.run(recoverySuite);
    return jsunity.writeDone().status ? 0 : 1;
  }
}
<commit_msg>adjust test for slow Jenkins<commit_after>/* jshint globalstrict:false, strict:false, unused : false */
/* global fail, assertTrue, assertFalse, assertEqual, TRANSACTION */
// //////////////////////////////////////////////////////////////////////////////
// / @brief tests for transactions
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2010-2012 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Jan Steemann
// / @author Copyright 2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////

var db = require('@arangodb').db;
var internal = require('internal');
var FoxxService = require('@arangodb/foxx/service');
var jsunity = require('jsunity');
var fs = require('fs');

function runSetup () {
  'use strict';
  internal.debugClearFailAt();

  let appPath = fs.join(FoxxService._appPath, '..');

  try {
    db._dropDatabase('UnitTestsRecovery1');
  } catch (err) {}

  db._createDatabase('UnitTestsRecovery1');

  fs.write(fs.join(appPath, 'UnitTestsRecovery1', 'foo.json'), 'test');

  try {
    db._dropDatabase('UnitTestsRecovery2');
  } catch (err) {}

  db._createDatabase('UnitTestsRecovery2');

  fs.write(fs.join(appPath, 'UnitTestsRecovery2', 'bar.json'), 'test');

  db._dropDatabase('UnitTestsRecovery2');

  // we need to wait long enough for the DatabaseManagerThread to
  // physically carry out the deletion
  let path = fs.join(appPath, 'UnitTestsRecovery2');
  let tries = 0;
  while (++tries < 30) {
    if (!fs.isDirectory(path)) {
      break;
    }
    internal.wait(1, false);
  }
  internal.debugTerminate('crashing server');
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief test suite
// //////////////////////////////////////////////////////////////////////////////

function recoverySuite () {
  'use strict';
  jsunity.jsUnity.attachAssertions();

  return {
    setUp: function () {},
    tearDown: function () {},

    // //////////////////////////////////////////////////////////////////////////////
    // / @brief test whether the data are correct after restart
    // //////////////////////////////////////////////////////////////////////////////

    testFoxxDirectories: function () {
      var appPath = fs.join(FoxxService._appPath, '..');

      assertTrue(fs.isDirectory(fs.join(appPath, 'UnitTestsRecovery1')));
      assertTrue(fs.isFile(fs.join(appPath, 'UnitTestsRecovery1', 'foo.json')));

      assertFalse(fs.isDirectory(fs.join(appPath, 'UnitTestsRecovery2')));
      assertFalse(fs.isFile(fs.join(appPath, 'UnitTestsRecovery2', 'bar.json')));
    }

  };
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief executes the test suite
// //////////////////////////////////////////////////////////////////////////////

function main (argv) {
  'use strict';
  if (argv[1] === 'setup') {
    runSetup();
    return 0;
  } else {
    jsunity.run(recoverySuite);
    return jsunity.writeDone().status ? 0 : 1;
  }
}
<|endoftext|>


<commit_before><commit_msg>Clamp zoom<commit_after><|endoftext|>


<commit_before>var ballX = 75;
var ballY = 75;
var ballSpeedX = 5;
var ballSpeedY = 5;

// 1/20/2019
// section 4
// since re-factoring is happening I split into separate file
// which git is upposed to make unnecessary but i did it anyway

const TRACK_W = 40;
const TRACK_H = 40;
const TRACK_GAP = 2;
const TRACK_COLS = 20;
const TRACK_ROWS = 15;

// 1 and 0s instead of true/false
// coulse also use other numbers for different assets like flags or trees
// so this is a semi-visual way to layout the track
var trackGrid = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ];

var trackLeft = 0;

var canvas, canvasContext;

var mouseX = 0;
var mouseY = 0;

function updateMousePos(evt) {
	var rect = canvas.getBoundingClientRect();
	var root = document.documentElement;

	mouseX = evt.clientX - rect.left - root.scrollLeft;
	mouseY = evt.clientY - rect.top - root.scrollTop;

}

window.onload = function() {
    canvas = document.getElementById('gameCanvas');
    canvasContext = canvas.getContext('2d');

    var framesPerSecond = 30;
    setInterval(updateAll, 1000/framesPerSecond);

    canvas.addEventListener('mousemove', updateMousePos);

    ballReset();
}

function updateAll() {
    moveAll();
    drawAll();
}

function ballReset() {
    ballX = canvas.width / 2;
    ballY = canvas.height / 2;
}

// part of re-factoring in section 4

function ballMove() {
    ballX += ballSpeedX;
    ballY += ballSpeedY;

     if (ballX  < 0 && ballSpeedX < 0.0 ) {  // left -- modified in section 5.52
         ballSpeedX *= -1;
     }

     if (ballX > canvas.width && ballSpeedX > 0.0 ) { // right -- modified in section 5.52
         ballSpeedX *= -1;
     }

     if (ballY < 0 && ballSpeedY < 0.0 ) {   // top -- modified in section 5.52
         ballSpeedY *= -1;
     }

     if (ballY > canvas.height) { // bottom
         ballReset();
       //  trackReset(); // added section 5.49
     }
}

function isTrackAtColRow(col, row) {
    if (col >= 0 && col < TRACK_COLS &&
        row >= 0 && row < TRACK_ROWS) {

        var trackIndexUnderCoord = rowColToArrayIndex(col, row);
        return (trackGrid[trackIndexUnderCoord] == 1);

    } else {
        return false;
    }
}

function ballTrackHandling() {
    var ballTrackCol = Math.floor(ballX / TRACK_W);
    var ballTrackRow = Math.floor(ballY / TRACK_H);
    var trackIndexUnderBall = rowColToArrayIndex(ballTrackCol, ballTrackRow);

   if (ballTrackCol >= 0 && ballTrackCol < TRACK_COLS &&
       ballTrackRow >= 0 && ballTrackRow < TRACK_ROWS) {

        if (isTrackAtColRow(ballTrackCol, ballTrackRow)) {

            var prevBallX = ballX - ballSpeedX;
            var prevBallY = ballY - ballSpeedY;
            var prevTrackCol = Math.floor(prevBallX / TRACK_W);
            var prevTrackRow = Math.floor(prevBallY / TRACK_H);

            var bothTestsFailed = true;

            if (prevTrackCol != ballTrackCol) {
                if (isTrackAtColRow(prevTrackCol, ballTrackRow) == false)
                {
                    ballSpeedX *= -1
                    bothTestsFailed = false;
                }

            }

            if (prevTrackRow != ballTrackRow) {
                if (isTrackAtColRow(ballTrackCol, prevTrackRow) == false) {
                    ballSpeedY *= -1;
                    bothTestsFailed = false;
                }
            }

            if (bothTestsFailed) { // "armpit case" - passes through corner with no bouncing (section 44, around 5min30 in)
                ballSpeedX *= -1;
                ballSpeedY *= -1;
            }
        } // end of track found
   } // end of valid col and row

} // end of ballTrackHandling function

function moveAll() {

    ballMove();
    ballTrackHandling();
}

function rowColToArrayIndex(col, row) {
    return col + TRACK_COLS * row;
}

function drawTracks() {
    for (var eachRow = 0; eachRow < TRACK_ROWS; eachRow++) {
        for ( var eachCol = 0; eachCol < TRACK_COLS; eachCol++ ) {

            var arrayIndex = rowColToArrayIndex(eachCol, eachRow);

            if ( trackGrid[arrayIndex] == 1 ) {
                colorRect(TRACK_W * eachCol,TRACK_H*eachRow, TRACK_W-TRACK_GAP,TRACK_H - TRACK_GAP, 'blue');
            }

        }
    }
}

function drawAll() {

	colorRect(0,0, canvas.width,canvas.height, 'black'); // clear screen

	colorCircle(ballX,ballY, 10, 'white'); // draw ball

    drawTracks();

}

function colorRect(topLeftX, topLeftY, boxWidth, boxHeight, fillColor) {
    canvasContext.fillStyle= fillColor;
    canvasContext.fillRect(topLeftX,topLeftY, boxWidth,boxHeight);
}

function colorCircle(centerX,centerY, radius, fillColor) {
    canvasContext.fillStyle = fillColor;
    canvasContext.beginPath();
    canvasContext.arc(centerX,centerY, radius, 0, Math.PI*2, true);
    canvasContext.fill();
}

function colorText(showWords, textX,textY, fillColor) {
	canvasContext.fillStyle = fillColor;
	canvasContext.fillText(showWords, textX, textY);
}<commit_msg>sect 6.61: race track layout in 1s and 0s<commit_after>var ballX = 75;
var ballY = 75;
var ballSpeedX = 5;
var ballSpeedY = 5;

// 1/20/2019
// section 4
// since re-factoring is happening I split into separate file
// which git is upposed to make unnecessary but i did it anyway

const TRACK_W = 40;
const TRACK_H = 40;
const TRACK_GAP = 2;
const TRACK_COLS = 20;
const TRACK_ROWS = 15;

// 1 and 0s instead of true/false
// coulse also use other numbers for different assets like flags or trees
// so this is a semi-visual way to layout the track
var trackGrid = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
                 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1,
                 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1,
                 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
                 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
                 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
                 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
                 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
                 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1,
                 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,
                 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
                 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];

var trackLeft = 0;

var canvas, canvasContext;

var mouseX = 0;
var mouseY = 0;

function updateMousePos(evt) {
	var rect = canvas.getBoundingClientRect();
	var root = document.documentElement;

	mouseX = evt.clientX - rect.left - root.scrollLeft;
	mouseY = evt.clientY - rect.top - root.scrollTop;

}

window.onload = function() {
    canvas = document.getElementById('gameCanvas');
    canvasContext = canvas.getContext('2d');

    var framesPerSecond = 30;
    setInterval(updateAll, 1000/framesPerSecond);

    canvas.addEventListener('mousemove', updateMousePos);

    ballReset();
}

function updateAll() {
    moveAll();
    drawAll();
}

function ballReset() {
    ballX = canvas.width / 2;
    ballY = canvas.height / 2;
}

// part of re-factoring in section 4

function ballMove() {
    ballX += ballSpeedX;
    ballY += ballSpeedY;

     if (ballX  < 0 && ballSpeedX < 0.0 ) {  // left -- modified in section 5.52
         ballSpeedX *= -1;
     }

     if (ballX > canvas.width && ballSpeedX > 0.0 ) { // right -- modified in section 5.52
         ballSpeedX *= -1;
     }

     if (ballY < 0 && ballSpeedY < 0.0 ) {   // top -- modified in section 5.52
         ballSpeedY *= -1;
     }

     if (ballY > canvas.height) { // bottom
         ballReset();
       //  trackReset(); // added section 5.49
     }
}

function isTrackAtColRow(col, row) {
    if (col >= 0 && col < TRACK_COLS &&
        row >= 0 && row < TRACK_ROWS) {

        var trackIndexUnderCoord = rowColToArrayIndex(col, row);
        return (trackGrid[trackIndexUnderCoord] == 1);

    } else {
        return false;
    }
}

function ballTrackHandling() {
    var ballTrackCol = Math.floor(ballX / TRACK_W);
    var ballTrackRow = Math.floor(ballY / TRACK_H);
    var trackIndexUnderBall = rowColToArrayIndex(ballTrackCol, ballTrackRow);

   if (ballTrackCol >= 0 && ballTrackCol < TRACK_COLS &&
       ballTrackRow >= 0 && ballTrackRow < TRACK_ROWS) {

        if (isTrackAtColRow(ballTrackCol, ballTrackRow)) {

            var prevBallX = ballX - ballSpeedX;
            var prevBallY = ballY - ballSpeedY;
            var prevTrackCol = Math.floor(prevBallX / TRACK_W);
            var prevTrackRow = Math.floor(prevBallY / TRACK_H);

            var bothTestsFailed = true;

            if (prevTrackCol != ballTrackCol) {
                if (isTrackAtColRow(prevTrackCol, ballTrackRow) == false)
                {
                    ballSpeedX *= -1
                    bothTestsFailed = false;
                }

            }

            if (prevTrackRow != ballTrackRow) {
                if (isTrackAtColRow(ballTrackCol, prevTrackRow) == false) {
                    ballSpeedY *= -1;
                    bothTestsFailed = false;
                }
            }

            if (bothTestsFailed) { // "armpit case" - passes through corner with no bouncing (section 44, around 5min30 in)
                ballSpeedX *= -1;
                ballSpeedY *= -1;
            }
        } // end of track found
   } // end of valid col and row

} // end of ballTrackHandling function

function moveAll() {

    ballMove();
    ballTrackHandling();
}

function rowColToArrayIndex(col, row) {
    return col + TRACK_COLS * row;
}

function drawTracks() {
    for (var eachRow = 0; eachRow < TRACK_ROWS; eachRow++) {
        for ( var eachCol = 0; eachCol < TRACK_COLS; eachCol++ ) {

            var arrayIndex = rowColToArrayIndex(eachCol, eachRow);

            if ( trackGrid[arrayIndex] == 1 ) {
                colorRect(TRACK_W * eachCol,TRACK_H*eachRow, TRACK_W-TRACK_GAP,TRACK_H - TRACK_GAP, 'blue');
            }

        }
    }
}

function drawAll() {

	colorRect(0,0, canvas.width,canvas.height, 'black'); // clear screen

	colorCircle(ballX,ballY, 10, 'white'); // draw ball

    drawTracks();

}

function colorRect(topLeftX, topLeftY, boxWidth, boxHeight, fillColor) {
    canvasContext.fillStyle= fillColor;
    canvasContext.fillRect(topLeftX,topLeftY, boxWidth,boxHeight);
}

function colorCircle(centerX,centerY, radius, fillColor) {
    canvasContext.fillStyle = fillColor;
    canvasContext.beginPath();
    canvasContext.arc(centerX,centerY, radius, 0, Math.PI*2, true);
    canvasContext.fill();
}

function colorText(showWords, textX,textY, fillColor) {
	canvasContext.fillStyle = fillColor;
	canvasContext.fillText(showWords, textX, textY);
}<|endoftext|>