<commit_before>'use strict'
var Base = require('vigour-js/lib/base')
var Observable = require('vigour-js/lib/observable')
var Element = require('../')
var isNode = require('vigour-js/lib/util/is/node')
var Prop = require('../../property')

var render
if (!isNode) {
  render = require('../../engine/dom/render')
}

function $map (map, path, nolog) {
  if (this.storedmap) {
    return this.storedmap // not allways correct but good in 99% of the cases
  }
  // this can be optmized a lot!
  var maker
  if (!map) {
    path = []
    maker = true
    map = this.storedmap = {}
    if (this.$) {
      map.$ = this.$
      if (this.$collection) {
        map.$collection = this.ChildConstructor.prototype.$map(void 0, void 0, true)
      }
    } else {
      map.$context = true
    }
  }
  function each (prop, key) {
    var isProp = prop instanceof Prop
    if (prop.$) {
      var p = path.concat([key])
      var mymap = map
      if (!isProp && mymap.$context) {
        delete mymap.$context
      }
      for (var i = 0, len = p.length; i < len; i++) {
        if (mymap[p[i]]) {
          mymap = mymap[p[i]]
          if (!isProp && mymap.$context) {
            delete mymap.$context
          }
        } else {
          mymap = mymap[p[i]] = {}
          if (isProp) {
            mymap.$context = true
          }
        }
        if (i === p.length - 1) {
          mymap.$ = prop.$
          if (prop.$collection) {
            mymap.$collection = prop.ChildConstructor.prototype.$map(void 0, void 0, true)
          }
        }
      }
    }
    prop.$map(map, path.concat([key]))
  }
  this.each(each, (p) => p instanceof Element || p instanceof Prop)
  if (maker && !nolog) {
    console.log(JSON.stringify(map, false, 2))
  }
  return map
}

function defBind () {
  return (this._contextLevel === 1 ? this._context : this._parent).parseValue()
}

function getit (field) {
  // save ram by storing in a map !
  return function fieldbind () {
    var self
    var parent = this._contextLevel === 1 ? this._context : this._parent
    while (parent) {
      self = parent._input
      if (self) {
        let a = self.retrieve && self.retrieve(field)
        return a ? a.parseValue() : 'smurky:' + field
      }
      parent = parent._contextLevel === 1 ? parent._context : parent._parent
    }
    return ''
  }
}

Prop.prototype.set({
  properties: {
    $storedmap: true,
    $ (val) {
      this.$ = val
      if (val === true) {
        this.set(defBind)
      } else {
        this.set(getit(val))
      }
    }
  },
  define: {
    $map: $map,
    origin: {
      get () {
        // without context polution!
        var field = this.$
        var reference = this
        if (field) {
          let self
          let up = reference.parent
          while (up) {
            self = up.refOrigin
            if (self) {
              reference = self.retrieve(field)
              break
            }
            up = up.parent
          }
        } else {
          while (reference._input instanceof Base) {
            reference = reference._input
          }
        }
        return reference
      }
    }
  }
})

exports.define = {
  $map: $map,
  $subscribeProperty (p, target) {
    var element = this
    if (p.$) {
      let cntxt = element._context
      let level = element._contextLevel
      let subscontext = cntxt || element
      // element._marked = true
      target.subscribe(p.$, 'data', [ function (data, event) {
        // Element
        if (cntxt) {
          element._context = cntxt
          element._contextLevel = level
        }
        p._context = element
        p._contextLevel = level ? level + 1 : 1
        let node = element.getNode()
        if (node) {
          p.render(node, event, element)
        }
      }, subscontext ], subscontext.uid || element)
    }
  },
  renderData (target, oldVal, event, old$, map) {
    var element = this
    if (!map) {
      map = this.$map()
    }
    walk(map, element, target)
    function walk (obj, element, target, rerenderinherit) {
      if (!target) {
        // trarget is null unsubscribe --- target is different -- unsubscribe nested
        return
      }
      if (obj.$context) {
        // if(rerenderinherit) {
        //   console.log('prop have to clear or something', element.path, 'X', element._context._path)
        // }
        // console.warn('------>', 'hello!!! cntxt')
        for (let i in obj) {
          if (element[i] && element[i] instanceof Prop) {
            element.$subscribeProperty(element[i], target)
          } else {
            if (element[i]) {
              walk(obj[i], element[i], target)
            }
          }
        }
      } else {
        if (element._context) {
          element = element.resolveContext(void 0, event, element._context) // moet nog met false
        }

        if (element.listensOnAttach && element._input !== target) {
          element.listensOnAttach.each(function (p, i) {
            // this can become 1000x faster (also dont do for nested)
            var keep
            for (var j in p.attach) {
              if (p.attach[j] && p.attach[j][1] === element) {
                // if (p.attach[j][0] === thisone) {
                //   keep = true
                // }
              }
            }
            // have to handle collection as well!
            if (!keep) {
              element.listensOnAttach[i].remove()
            }
          })
        }

        if (obj.$) {
          if (obj.$collection) {
            target.subscribe(obj.$, 'property', [
              function (data, event) {
                console.error('--------', this._path, element._path, obj.$)
                var origin = this.origin
                if (origin._input === null) {
                  element.each(function (p) {
                    p instanceof Element && p.remove(event)
                  })
                } else {
                  if (element._input !== origin) {
                    element.each(function (p) {
                      p instanceof Element && p.remove(event)
                    })
                  } else if (data.removed) {
                    for (let n in data.removed) {
                      console.log('---->', data.removed)
                      if (element[data.removed[n]]) {
                        element[data.removed[n]].remove(event)
                      }
                    }
                  }
                  element._input = origin
                  origin.each(function (property, key) {
                    var elem = element[key]
                    var origin = property.origin
                    if ((!elem || elem.input !== origin) && origin._input !== null) {
                      console.log('lettttz make!', key, origin._input)
                      // if second lets unsubscribe everything
                      // all listeners in attach on the target!
                      // make a function for this -- removeAllAttach(Element) // this way handle on data works for elements and collos
                      element.set({ [key]: origin }, event)
                      if (!element[key].$) {
                        element[key].renderData(origin, oldVal, event)
                      }
                    }
                  })
                }
              }, element
            ], element.uid, true, event)
          } else {
            // console.log('go go go', obj.$)
            // console.log('subscribe bitch')
            // first --- different
            let stamp = event.stamp
            target.subscribe(obj.$, 'data', [
              function thisone (data, event) {
                console.log('change listener AGAIN!', element._path)
                var origin = this.origin
                var rerender
                // this check is not enough!
                if (element._input && this.origin !== element._input || (stamp === event.stamp && oldVal)) {
                  if (element.listensOnAttach) {
                    element.listensOnAttach.each(function (p, i) {
                      // this can become 1000x faster (also dont do for nested)
                      var keep
                      for (var j in p.attach) {
                        if (p.attach[j] && p.attach[j][1] === element) {
                          if (p.attach[j][0] === thisone) {
                            keep = true
                          }
                        }
                      }
                      // have to handle collection as well!
                      if (!keep) {
                        element.listensOnAttach[i].remove()
                      }
                    })
                  }
                  console.log('SWITCH')
                  if (origin && origin._input !== null && !isNode) {
                    rerender = element.getNode()
                  }
                }
                element._input = origin //eslint-disable-line
                for (let i in obj) {
                  if (element[i] && element[i] instanceof Prop) {
                    element.$subscribeProperty(element[i], origin)
                  } else if (i !== '$') {
                    // console.error('rewalk!', i)
                    walk(obj[i], element[i], origin, rerender)
                  }
                }
                if (rerender && !rerenderinherit) {
                  console.error('!!!rerender!!!') // only do for top!
                  render(element, rerender.parentNode, Element, event, element.engine)
                }
              }, element
            ], element.uid, true, event)
          }
        } else {
          for (let i in obj) {
            if (i !== '$' && i !== '$collection') {
              if (element[i] && element[i] instanceof Prop) {
                element.$subscribeProperty(element[i], target)
              } else {
                walk(obj[i], element[i], target, rerenderinherit)
              }
            }
          }
        }
      }
    }
    // return
  },
  handleReference (val, event, oldVal) {
    var valIsObservable = val instanceof Observable
    if (valIsObservable) {
      this._input = val
      if (this.$) {
        this.renderData(val, oldVal, event)
      }
    } else if (oldVal instanceof Observable) {
      if (this.$) {
        this.renderData(val, oldVal, event)
      }
    }
  }
}

exports.properties = {
  $storedmap: true,
  $collection (val, event) {
    this.$collection = val
    this.properties.$.call(this, val, event)
  },
  $ (val, event) {
    this.$ = val
  }
}
<commit_msg>index<commit_after>'use strict'
var Base = require('vigour-js/lib/base')
var Observable = require('vigour-js/lib/observable')
var Element = require('../')
var isNode = require('vigour-js/lib/util/is/node')
var Prop = require('../../property')

var render
if (!isNode) {
  render = require('../../engine/dom/render')
}

function $map (map, path, nolog) {
  if (this.storedmap) {
    return this.storedmap // not allways correct but good in 99% of the cases
  }
  // this can be optmized a lot!
  var maker
  if (!map) {
    path = []
    maker = true
    map = this.storedmap = {}
    if (this.$) {
      map.$ = this.$
      if (this.$collection) {
        map.$collection = this.ChildConstructor.prototype.$map(void 0, void 0, true)
      }
    } else {
      map.$context = true
    }
  }
  function each (prop, key) {
    var isProp = prop instanceof Prop
    if (prop.$) {
      var p = path.concat([key])
      var mymap = map
      if (!isProp && mymap.$context) {
        delete mymap.$context
      }
      for (var i = 0, len = p.length; i < len; i++) {
        if (mymap[p[i]]) {
          mymap = mymap[p[i]]
          if (!isProp && mymap.$context) {
            delete mymap.$context
          }
        } else {
          mymap = mymap[p[i]] = {}
          if (isProp) {
            mymap.$context = true
          }
        }
        if (i === p.length - 1) {
          mymap.$ = prop.$
          if (prop.$collection) {
            mymap.$collection = prop.ChildConstructor.prototype.$map(void 0, void 0, true)
          }
        }
      }
    }
    prop.$map(map, path.concat([key]))
  }
  this.each(each, (p) => p instanceof Element || p instanceof Prop)
  if (maker && !nolog) {
    // console.log(JSON.stringify(map, false, 2))
  }
  return map
}

function defBind () {
  return (this._contextLevel === 1 ? this._context : this._parent).parseValue()
}

function getit (field) {
  // save ram by storing in a map !
  return function fieldbind () {
    var self
    var parent = this._contextLevel === 1 ? this._context : this._parent
    while (parent) {
      self = parent._input
      if (self) {
        let a = self.retrieve && self.retrieve(field)
        return a ? a.parseValue() : 'smurky:' + field
      }
      parent = parent._contextLevel === 1 ? parent._context : parent._parent
    }
    return ''
  }
}

Prop.prototype.set({
  properties: {
    $storedmap: true,
    $ (val) {
      this.$ = val
      if (val === true) {
        this.set(defBind)
      } else {
        this.set(getit(val))
      }
    }
  },
  define: {
    $map: $map,
    origin: {
      get () {
        // without context polution!
        var field = this.$
        var reference = this
        if (field) {
          let self
          let up = reference.parent
          while (up) {
            self = up.refOrigin
            if (self) {
              reference = self.retrieve(field)
              break
            }
            up = up.parent
          }
        } else {
          while (reference._input instanceof Base) {
            reference = reference._input
          }
        }
        return reference
      }
    }
  }
})

exports.define = {
  $map: $map,
  $subscribeProperty (p, target) {
    var element = this
    if (p.$) {
      let cntxt = element._context
      let level = element._contextLevel
      let subscontext = cntxt || element
      // element._marked = true
      target.subscribe(p.$, 'data', [ function (data, event) {
        // Element
        if (cntxt) {
          element._context = cntxt
          element._contextLevel = level
        }
        p._context = element
        p._contextLevel = level ? level + 1 : 1
        let node = element.getNode()
        if (node) {
          p.render(node, event, element)
        }
      }, subscontext ], subscontext.uid || element)
    }
  },
  renderData (target, oldVal, event, old$, map) {
    var element = this
    if (!map) {
      map = this.$map()
    }
    walk(map, element, target)
    function walk (obj, element, target, rerenderinherit) {
      if (!target) {
        // trarget is null unsubscribe --- target is different -- unsubscribe nested
        return
      }
      if (obj.$context) {
        // if(rerenderinherit) {
        //   console.log('prop have to clear or something', element.path, 'X', element._context._path)
        // }
        // console.warn('------>', 'hello!!! cntxt')
        for (let i in obj) {
          if (element[i] && element[i] instanceof Prop) {
            element.$subscribeProperty(element[i], target)
          } else {
            if (element[i]) {
              walk(obj[i], element[i], target)
            }
          }
        }
      } else {
        if (element._context) {
          element = element.resolveContext(void 0, event, element._context) // moet nog met false
        }

        if (element.listensOnAttach && element._input !== target) {
          element.listensOnAttach.each(function (p, i) {
            // this can become 1000x faster (also dont do for nested)
            var keep
            for (var j in p.attach) {
              if (p.attach[j] && p.attach[j][1] === element) {
                // if (p.attach[j][0] === thisone) {
                //   keep = true
                // }
              }
            }
            // have to handle collection as well!
            if (!keep) {
              element.listensOnAttach[i].remove()
            }
          })
        }

        if (obj.$) {
          if (obj.$collection) {
            target.subscribe(obj.$, 'property', [
              function (data, event) {
                // console.error('--------', this._path, element._path, obj.$)
                var origin = this.origin
                if (origin._input === null) {
                  element.each(function (p) {
                    p instanceof Element && p.remove(event)
                  })
                } else {
                  if (element._input !== origin) {
                    element.each(function (p) {
                      p instanceof Element && p.remove(event)
                    })
                  } else if (data.removed) {
                    for (let n in data.removed) {
                      // console.log('---->', data.removed)
                      if (element[data.removed[n]]) {
                        element[data.removed[n]].remove(event)
                      }
                    }
                  }
                  element._input = origin
                  origin.each(function (property, key) {
                    var elem = element[key]
                    var origin = property.origin
                    if ((!elem || elem.input !== origin) && origin._input !== null) {
                      // console.log('lettttz make!', key, origin._input)
                      // if second lets unsubscribe everything
                      // all listeners in attach on the target!
                      // make a function for this -- removeAllAttach(Element) // this way handle on data works for elements and collos
                      element.set({ [key]: origin }, event)
                      if (!element[key].$) {
                        element[key].renderData(origin, oldVal, event)
                      }
                    }
                  })
                }
              }, element
            ], element.uid, true, event)
          } else {
            // console.log('go go go', obj.$)
            // console.log('subscribe bitch')
            // first --- different
            let stamp = event.stamp
            target.subscribe(obj.$, 'data', [
              function thisone (data, event) {
                console.log('change listener AGAIN!', element._path)
                var origin = this.origin
                var rerender
                // this check is not enough!
                if (element._input && this.origin !== element._input || (stamp === event.stamp && oldVal)) {
                  if (element.listensOnAttach) {
                    element.listensOnAttach.each(function (p, i) {
                      // this can become 1000x faster (also dont do for nested)
                      var keep
                      for (var j in p.attach) {
                        if (p.attach[j] && p.attach[j][1] === element) {
                          if (p.attach[j][0] === thisone) {
                            keep = true
                          }
                        }
                      }
                      // have to handle collection as well!
                      if (!keep) {
                        element.listensOnAttach[i].remove()
                      }
                    })
                  }
                  console.log('SWITCH')
                  if (origin && origin._input !== null && !isNode) {
                    rerender = element.getNode()
                  }
                }
                element._input = origin //eslint-disable-line
                for (let i in obj) {
                  if (element[i] && element[i] instanceof Prop) {
                    element.$subscribeProperty(element[i], origin)
                  } else if (i !== '$') {
                    // console.error('rewalk!', i)
                    walk(obj[i], element[i], origin, rerender)
                  }
                }
                if (rerender && !rerenderinherit) {
                  console.error('!!!rerender!!!') // only do for top!
                  render(element, rerender.parentNode, Element, event, element.engine)
                }
              }, element
            ], element.uid, true, event)
          }
        } else {
          for (let i in obj) {
            if (i !== '$' && i !== '$collection') {
              if (element[i] && element[i] instanceof Prop) {
                element.$subscribeProperty(element[i], target)
              } else {
                walk(obj[i], element[i], target, rerenderinherit)
              }
            }
          }
        }
      }
    }
    // return
  },
  handleReference (val, event, oldVal) {
    var valIsObservable = val instanceof Observable
    if (valIsObservable) {
      this._input = val
      if (this.$) {
        this.renderData(val, oldVal, event)
      }
    } else if (oldVal instanceof Observable) {
      if (this.$) {
        this.renderData(val, oldVal, event)
      }
    }
  }
}

exports.properties = {
  $storedmap: true,
  $collection (val, event) {
    this.$collection = val
    this.properties.$.call(this, val, event)
  },
  $ (val, event) {
    this.$ = val
  }
}
<|endoftext|>


<commit_before>'use strict'

var config = require('../config')

function generateMetadataElevmappe (options) {
  if (!options) {
    throw new Error('Missing required input: options')
  }
  if (!options.fullName) {
    throw new Error('Missing required input: options.fullName')
  }
  if (!options.personalIdNumber) {
    throw new Error('Missing required input: options.personalIdNumber')
  }

  var meta = {
    'clientService': 'CaseService',
    'clientMethod': 'CreateCase',
    'args': {
      'parameter': {
        'AccessCode': '13', // Codetable: Accesscode
        'AccessGroup': 'RIM-elever', // Tilgangsgruppe navn
        'ArchiveCodes': [
          {
            'ClassCodeParameter': [
              {
                'ArchiveCode': options.personalIdNumber,
                'ArchiveType': 'Fnr', // Codetable: Noark classification
                'IsManualText': true,
                'Sort': '1'
              },
              {
                'ArchiveCode': 'A56', // Codetable: Noark classification code
                'ArchiveType': 'FAGKODE PRINSIPP', // Codetable: Noark classification
                'Sort': '2'
              },
              {
                'ArchiveCode': '201757',
                'ArchiveType': 'FELLESKODE PRINSIPP',
                'Sort': '3'
              },
              {
                'ArchiveCode': '26118645145',
                'ArchiveType': 'Emneorientert arkivnøkkel',
                'IsManualText': true,
                'Sort': '4'
              }
            ]
          }
        ],
        'Contacts': [
          {
            'CaseContactParameter': {
              'ReferenceNumber': options.personalIdNumber,
              'Role': '9' // Codetable: Contact - Case Role
            }
          }
        ],
        'Paragraph': 'Offl §13 jfr Fvl §13', // Codetable: Paragraph
        'ResponsibleEnterpriseRecno': config.minelev.main_responsibleEnterpriseRecno, // Recnr ansvarlig virksomhet
        'ResponsiblePersonRecno': config.minelev.main_responsiblePersonRecno,
        'Status': 'B', // Under behandling
        'SubArchive': '200002', // Codetable: Noark subarchive
        'Title': 'Elevmappe',
        'UnofficialTitle': 'Elevmappe - ' + options.fullName
      }
    }
  }
  return meta
}

module.exports = generateMetadataElevmappe
<commit_msg>Update elevmappe-add-case.js<commit_after>'use strict'

var config = require('../config')

function generateMetadataElevmappe (options) {
  if (!options) {
    throw new Error('Missing required input: options')
  }
  if (!options.fullName) {
    throw new Error('Missing required input: options.fullName')
  }
  if (!options.personalIdNumber) {
    throw new Error('Missing required input: options.personalIdNumber')
  }

  var meta = {
    'clientService': 'CaseService',
    'clientMethod': 'CreateCase',
    'args': {
      'parameter': {
        'AccessCode': '13', // Codetable: Accesscode
        'AccessGroup': 'RIM-elever', // Tilgangsgruppe navn
        'ArchiveCodes': [
          {
            'ClassCodeParameter': [
              {
                'ArchiveCode': options.personalIdNumber,
                'ArchiveType': 'Fnr', // Codetable: Noark classification
                'IsManualText': true,
                'Sort': '1'
              },
              {
                'ArchiveCode': 'A56', // Codetable: Noark classification code
                'ArchiveType': 'FAGKODE PRINSIPP', // Codetable: Noark classification
                'Sort': '2'
              },
              {
                'ArchiveCode': '201757',
                'ArchiveType': 'FELLESKODE PRINSIPP',
                'Sort': '3'
              },
              {
                'ArchiveCode': options.personalIdNumber,
                'ArchiveType': 'Emneorientert arkivnøkkel',
                'IsManualText': true,
                'Sort': '4'
              }
            ]
          }
        ],
        'Contacts': [
          {
            'CaseContactParameter': {
              'ReferenceNumber': options.personalIdNumber,
              'Role': '9' // Codetable: Contact - Case Role
            }
          }
        ],
        'Paragraph': 'Offl §13 jfr Fvl §13', // Codetable: Paragraph
        'ResponsibleEnterpriseRecno': config.minelev.main_responsibleEnterpriseRecno, // Recnr ansvarlig virksomhet
        'ResponsiblePersonRecno': config.minelev.main_responsiblePersonRecno,
        'Status': 'B', // Under behandling
        'SubArchive': '200002', // Codetable: Noark subarchive
        'Title': 'Elevmappe',
        'UnofficialTitle': 'Elevmappe - ' + options.fullName
      }
    }
  }
  return meta
}

module.exports = generateMetadataElevmappe
<|endoftext|>


<commit_before>/* global $, jQuery, AC, config */
/* eslint prefer-reflect: "off" */

/*
 * Standard helper functions across lib.reviews pages, divided into jQuery
 * plugins and more lib.reviews-specific functionality.
 * @license CC-0; see https://creativecommons.org/publicdomain/zero/1.0/
 */

(function($) {
  'use strict';

  // Return any empty input elements among a selector of inputs
  $.fn.getEmptyInputs = function() {
    return this.filter(function() {
      return this.value === undefined || String(this.value) === '';
    });
  };

  // Highlight labels associated with a set of elements using an indicator
  // class. Typically used to highlight required form fields, in combination
  // with emptyInputs(). This will not hide previously set labels.
  $.fn.highlightLabels = function(indicatorSelector) {

    if (!indicatorSelector)
      indicatorSelector = "span.required";

    // Show the ones from our current jQuery object
    this.each(function() {
      $(`label[for="${this.id}"] ${indicatorSelector}`).show();
    });

    // Return the (unmodified) object for further processing/chaining as needed
    return this;
  };

  $.fn.attachRequiredFieldHandler = function(options) {

    if (!options)
      options = {};

    // How we find "* required" indicators that are child elements of labels
    let indicatorSelector = options.indicatorSelector || 'span.required';

    // Error message(s) selector informing user required fields are missing
    let requiredFieldsMessage = options.requiredFieldMessage || '#required-fields-message';

    // Error message(s) selector reminding user about other validation errors
    let formErrorMessage = options.formErrorMessage || '#form-error-message';

    // Selector used to find any remaining validation errors that need to be corrected
    let validationErrorSelector = options.validationErrorSelector || '.validation-error:visible';

    this.click(requiredFieldHandler);

    function requiredFieldHandler(event) {

      // Clear out old warnings
      $(`${requiredFieldsMessage},${formErrorMessage},label ${indicatorSelector}`).hide();

      let $emptyFields = $('input[data-required],textarea[data-required]')
        .getEmptyInputs()
        .highlightLabels();

      if ($emptyFields.length) {
        $(requiredFieldsMessage).show();
        event.preventDefault();
        return;
      }

      // Highlight any other validation errors now (not doing it earlier to avoid
      // error message overkill)
      if ($(validationErrorSelector).length > 0) {
        $(formErrorMessage).show();
        event.preventDefault();
        return;
      }

    }

  };

}(jQuery));

(function() {
  'use strict';
  // Global namespace for lib.reviews-specific functions
  window.libreviews = {};

  window.libreviews.trimInput = function() {
    this.value = this.value.trim();
  };

  // For groups of form controls that are sometimes, but not always, required
  // inputs, we can enable the required check selectively. To do so, we need
  // to set the "data-required-indicator-group" and "data-required-input-group"
  // to a unique ID for each group, and set the data-enable-required-group
  // and data-disable-required-group to the same ID for the controls which
  // change the state (for now only radio controls are supported).
  // The indicator also needs to have the "hidden" class initially.
  window.libreviews.enableRequiredGroup = function(groupID) {
    $(`span[data-required-indicator-group="${groupID}"]`)
      .addClass('required')
      .removeClass('hidden');
    $(`[data-required-input-group="${groupID}"]`)
      .attr('data-required', '');
  };

  window.libreviews.disableRequiredGroup = function(groupID) {
    $(`span[data-required-indicator-group="${groupID}"]`)
      .addClass('hidden')
      .removeClass('required');
    $(`[data-required-input-group="${groupID}"]`)
      .removeAttr('data-required');
  };

  $('input[type="radio"][data-enable-required-group]').focus(function() {
    window.libreviews.enableRequiredGroup($(this).attr('data-enable-required-group'));
  });

  $('input[type="radio"][data-disable-required-group]').focus(function() {
    window.libreviews.disableRequiredGroup($(this).attr('data-disable-required-group'));
  });

  // Generic function for dismiss buttons. The attribute data-dismiss-element
  // must specify an element ID.
  $('button[data-dismiss-element]').click(function(event) {
    let id = $(this).attr('data-dismiss-element');
    $(`#${id}`).fadeOut(200);
    event.preventDefault();
  });

  // Generic function to permanently dismiss notices of a certain kind.
  // This is useful for introductory messages, or community-wide banners.
  $('button[data-suppress-notice]').click(function(event) {
    let id = $(this).attr('data-suppress-notice');
    $.ajax({
        type: 'POST',
        url: `/api/actions/suppress-notice`,
        data: JSON.stringify({
          noticeType: id
        }),
        contentType: 'application/json',
        dataType: 'json'
      })
      .done(() => {
        $(`#${id}`).fadeOut(200);
      })
      .fail(() => {
        $('#generic-action-error').removeClass('hidden');
      });
    event.preventDefault();
  });

  // Prevents submission of buttons that have data-check-required when required
  // fields are missing
  $('button[data-check-required]').attachRequiredFieldHandler();

  // Auto-trim all inputs with data-auto-trim
  $('input[data-auto-trim],textarea[data-auto-trim]').change(window.libreviews.trimInput);

  // Add link anchors to long texts
  $('.long-text h2,.long-text h3').each(function() {
    $(this).prepend(`<a href="#${this.id}" class="fragment-link no-print"><span class="fa fa-link"></span></a>`);
  });

  // Dynamic help sidebars

  // Exported repaint function, trigger this if you need to repaint the
  // help because of a change in scroll position. This is done automatically
  // for window resizes.
  window.libreviews.repaintFocusedHelp = () => {
    let focused = $(':focus')[0];

    // Check if the currently focused element requires a help re-render
    if (focused && focused.id && $(`[data-help-for=${focused.id}]`).length)
      showInputHelp.apply(focused);

  };

  if ($('[data-help-for]').length) {

    // Attach dynamic help display for data-help-for="id" elements (to the "id"
    // element, typically an input)
    $('[data-help-for]').each(function() {
      let inputID = $(this).attr('data-help-for');
      let $input = $(`#${inputID}`);
      $input.focus(showInputHelp);
      $input.blur(hideInputHelp);
      // Re-calculate positioning of help in case of resizes
      if (typeof MutationObserver !== 'undefined') {
        new MutationObserver(window.libreviews.repaintFocusedHelp).observe($input[0], {
          attributes: true,
          attributeFilter: ['style']
        });
      }
    });

    // Re-calculate positioning of help on window resize
    $(window).resize(window.libreviews.repaintFocusedHelp);

  }

  // Show/hide parts of a page dynamically
  $('[data-show]').focus(function() {
    $(`#${$(this).attr('data-show')}`).slideDown(200);
  });

  $('[data-hide]').focus(function() {
    $(`#${$(this).attr('data-hide')}`).slideUp(200);
  });

  // Focus input
  $('[data-focus]').focus();

  // Buttons that copy text into the clipboard
  $('[data-copy]').click(function() {
    let copySourceID = $(this).attr('data-copy');
    let copySource = $(`#${copySourceID}`)[0];

    let range = document.createRange();
    range.selectNode(copySource);
    window.getSelection().removeAllRanges();
    window.getSelection().addRange(range);
    try {
      document.execCommand('copy');
    } catch (error) {
      console.error('Copying not supported in your browser.');
    }
  });

  // Some click handlers exist inside content which may be dynamically generated.
  window.libreviews.updateContentClickHandlers = () => {
    $('summary.content-warning-notice').click(toggleDangerousContent);
  };

  window.libreviews.updateContentClickHandlers();

  // Add autocompletion to search box if present
  if ($('#search-input').length)
    setupSearch();

  // For content which is hidden by default and can be expanded, e.g., spoiler
  // warnings, NSFW warnings
  function toggleDangerousContent(event) {
    if ($(this).parent().is('[open]')) {
      $(this).next('.dangerous-content').slideUp(200, () => {
        $(this).parent().removeAttr('open');
      });
    } else {
      $(this).parent().attr('open', '');
      $(this).next('.dangerous-content').slideDown(200);
    }
    event.preventDefault();
  }

  function setupSearch() {
    let ac = new AC($('#search-input')[0], null, queryFn, null, null, triggerFn);
    ac.secondaryTextKey = 'language';

    function triggerFn(row) {
      if (row.urlID)
        window.location = `/${row.urlID}`;
    }

    function queryFn(query) {
      this.results = [];
      query = query.trim();
      if (query) {
        $
          .get(`/api/suggest/thing/${encodeURIComponent(query)}`)
          .done(res => {
            if (res.results) {

              // We don't want to show any suggestion more than once, even
              // if it appears in multiple languages
              let seenIDs = [];

              // Helper function for adding labels to the suggestions array
              let processLabelKey = (labelKey, labelLanguage) => {
                for (let label of res.results[labelKey]) {
                  // Don't include any result more than once
                  if (seenIDs.indexOf(label._id) !== -1)
                    continue;
                  seenIDs.push(label._id);

                  let suggestion = {
                    title: label.text,
                    urlID: label.urlID
                  };
                  if (labelLanguage !== config.language)
                    suggestion.language = labelLanguage;

                  this.results.push(suggestion);
                }
                Reflect.deleteProperty(res.results, labelKey);
              };

              // Process the user's currently selected language before others
              let myLabelKey = `labels-${config.language}`;
              if (Array.isArray(res.results[myLabelKey]) && res.results[myLabelKey].length)
                processLabelKey(myLabelKey, config.language);

              // Process remaining languages
              for (let labelKey in res.results) {
                let labelLanguage = (labelKey.match(/labels-(.*)/) || [])[1];
                if (!labelLanguage)
                  continue;
                processLabelKey(labelKey, labelLanguage);
              }
            }
            this.render();
          });
      } else {
        this.render();
      }
    }
  }

  function showInputHelp() {
    let id = this.id;
    // Hide all help texts
    $('.help-text').hide();

    // Show help text for active input
    $(`#${id}-help`).show();

    // We want to avoid pushing down the next form field, so we use
    // absolute positioning if available.
    if (this.getBoundingClientRect && $(`label[for=${id}]`)[0]) {
      let posHelp, posLabel;
      posLabel = $(`label[for=${id}]`)[0].getBoundingClientRect();
      posHelp = $(`#${id}-help`)[0].getBoundingClientRect();

      // Recaculate boundaries for all inputs in case fields have been resized,
      // e.g., textarea via resize triangle. We don't want any help text to
      // start before the rightmost one, so that it doesn't bleed into the
      // control below it
      let maxRight;
      $(this)
        .parents('form')
        .find('input,textarea')
        .each(function() {
          let eleRight = this.getBoundingClientRect().right;
          if (maxRight === undefined || maxRight < eleRight)
            maxRight = eleRight;
        });

      if (posHelp.left > posLabel.right && window.innerWidth > posLabel.width + posHelp.width) {
        // Position vertically aligned with the input we're showing help for
        let newTopPos = Math.floor(window.scrollY) + Math.floor(posLabel.top);
        let newLeftPos = maxRight + 5;
        let style = `position:absolute;top:${newTopPos}px;display:inline-block;left:${newLeftPos}px;`;
        $(`#${id}-help`).attr('style', style);
      } else {
        // Reset position
        $(`#${id}-help`).attr('style', 'display:inline-block;');
      }
    }
  }

  function hideInputHelp() {
    let id = this.id;
    // Keep help visible if user is hoving over it, so
    // links remain accessible
    if (!$('.help-text:hover').length)
      $(`#${id}-help`).hide();
  }

}());
<commit_msg>Autocomplete tweak<commit_after>/* global $, jQuery, AC, config */
/* eslint prefer-reflect: "off" */

/*
 * Standard helper functions across lib.reviews pages, divided into jQuery
 * plugins and more lib.reviews-specific functionality.
 * @license CC-0; see https://creativecommons.org/publicdomain/zero/1.0/
 */

(function($) {
  'use strict';

  // Return any empty input elements among a selector of inputs
  $.fn.getEmptyInputs = function() {
    return this.filter(function() {
      return this.value === undefined || String(this.value) === '';
    });
  };

  // Highlight labels associated with a set of elements using an indicator
  // class. Typically used to highlight required form fields, in combination
  // with emptyInputs(). This will not hide previously set labels.
  $.fn.highlightLabels = function(indicatorSelector) {

    if (!indicatorSelector)
      indicatorSelector = "span.required";

    // Show the ones from our current jQuery object
    this.each(function() {
      $(`label[for="${this.id}"] ${indicatorSelector}`).show();
    });

    // Return the (unmodified) object for further processing/chaining as needed
    return this;
  };

  $.fn.attachRequiredFieldHandler = function(options) {

    if (!options)
      options = {};

    // How we find "* required" indicators that are child elements of labels
    let indicatorSelector = options.indicatorSelector || 'span.required';

    // Error message(s) selector informing user required fields are missing
    let requiredFieldsMessage = options.requiredFieldMessage || '#required-fields-message';

    // Error message(s) selector reminding user about other validation errors
    let formErrorMessage = options.formErrorMessage || '#form-error-message';

    // Selector used to find any remaining validation errors that need to be corrected
    let validationErrorSelector = options.validationErrorSelector || '.validation-error:visible';

    this.click(requiredFieldHandler);

    function requiredFieldHandler(event) {

      // Clear out old warnings
      $(`${requiredFieldsMessage},${formErrorMessage},label ${indicatorSelector}`).hide();

      let $emptyFields = $('input[data-required],textarea[data-required]')
        .getEmptyInputs()
        .highlightLabels();

      if ($emptyFields.length) {
        $(requiredFieldsMessage).show();
        event.preventDefault();
        return;
      }

      // Highlight any other validation errors now (not doing it earlier to avoid
      // error message overkill)
      if ($(validationErrorSelector).length > 0) {
        $(formErrorMessage).show();
        event.preventDefault();
        return;
      }

    }

  };

}(jQuery));

(function() {
  'use strict';
  // Global namespace for lib.reviews-specific functions
  window.libreviews = {};

  window.libreviews.trimInput = function() {
    this.value = this.value.trim();
  };

  // For groups of form controls that are sometimes, but not always, required
  // inputs, we can enable the required check selectively. To do so, we need
  // to set the "data-required-indicator-group" and "data-required-input-group"
  // to a unique ID for each group, and set the data-enable-required-group
  // and data-disable-required-group to the same ID for the controls which
  // change the state (for now only radio controls are supported).
  // The indicator also needs to have the "hidden" class initially.
  window.libreviews.enableRequiredGroup = function(groupID) {
    $(`span[data-required-indicator-group="${groupID}"]`)
      .addClass('required')
      .removeClass('hidden');
    $(`[data-required-input-group="${groupID}"]`)
      .attr('data-required', '');
  };

  window.libreviews.disableRequiredGroup = function(groupID) {
    $(`span[data-required-indicator-group="${groupID}"]`)
      .addClass('hidden')
      .removeClass('required');
    $(`[data-required-input-group="${groupID}"]`)
      .removeAttr('data-required');
  };

  $('input[type="radio"][data-enable-required-group]').focus(function() {
    window.libreviews.enableRequiredGroup($(this).attr('data-enable-required-group'));
  });

  $('input[type="radio"][data-disable-required-group]').focus(function() {
    window.libreviews.disableRequiredGroup($(this).attr('data-disable-required-group'));
  });

  // Generic function for dismiss buttons. The attribute data-dismiss-element
  // must specify an element ID.
  $('button[data-dismiss-element]').click(function(event) {
    let id = $(this).attr('data-dismiss-element');
    $(`#${id}`).fadeOut(200);
    event.preventDefault();
  });

  // Generic function to permanently dismiss notices of a certain kind.
  // This is useful for introductory messages, or community-wide banners.
  $('button[data-suppress-notice]').click(function(event) {
    let id = $(this).attr('data-suppress-notice');
    $.ajax({
        type: 'POST',
        url: `/api/actions/suppress-notice`,
        data: JSON.stringify({
          noticeType: id
        }),
        contentType: 'application/json',
        dataType: 'json'
      })
      .done(() => {
        $(`#${id}`).fadeOut(200);
      })
      .fail(() => {
        $('#generic-action-error').removeClass('hidden');
      });
    event.preventDefault();
  });

  // Prevents submission of buttons that have data-check-required when required
  // fields are missing
  $('button[data-check-required]').attachRequiredFieldHandler();

  // Auto-trim all inputs with data-auto-trim
  $('input[data-auto-trim],textarea[data-auto-trim]').change(window.libreviews.trimInput);

  // Add link anchors to long texts
  $('.long-text h2,.long-text h3').each(function() {
    $(this).prepend(`<a href="#${this.id}" class="fragment-link no-print"><span class="fa fa-link"></span></a>`);
  });

  // Dynamic help sidebars

  // Exported repaint function, trigger this if you need to repaint the
  // help because of a change in scroll position. This is done automatically
  // for window resizes.
  window.libreviews.repaintFocusedHelp = () => {
    let focused = $(':focus')[0];

    // Check if the currently focused element requires a help re-render
    if (focused && focused.id && $(`[data-help-for=${focused.id}]`).length)
      showInputHelp.apply(focused);

  };

  if ($('[data-help-for]').length) {

    // Attach dynamic help display for data-help-for="id" elements (to the "id"
    // element, typically an input)
    $('[data-help-for]').each(function() {
      let inputID = $(this).attr('data-help-for');
      let $input = $(`#${inputID}`);
      $input.focus(showInputHelp);
      $input.blur(hideInputHelp);
      // Re-calculate positioning of help in case of resizes
      if (typeof MutationObserver !== 'undefined') {
        new MutationObserver(window.libreviews.repaintFocusedHelp).observe($input[0], {
          attributes: true,
          attributeFilter: ['style']
        });
      }
    });

    // Re-calculate positioning of help on window resize
    $(window).resize(window.libreviews.repaintFocusedHelp);

  }

  // Show/hide parts of a page dynamically
  $('[data-show]').focus(function() {
    $(`#${$(this).attr('data-show')}`).slideDown(200);
  });

  $('[data-hide]').focus(function() {
    $(`#${$(this).attr('data-hide')}`).slideUp(200);
  });

  // Focus input
  $('[data-focus]').focus();

  // Buttons that copy text into the clipboard
  $('[data-copy]').click(function() {
    let copySourceID = $(this).attr('data-copy');
    let copySource = $(`#${copySourceID}`)[0];

    let range = document.createRange();
    range.selectNode(copySource);
    window.getSelection().removeAllRanges();
    window.getSelection().addRange(range);
    try {
      document.execCommand('copy');
    } catch (error) {
      console.error('Copying not supported in your browser.');
    }
  });

  // Some click handlers exist inside content which may be dynamically generated.
  window.libreviews.updateContentClickHandlers = () => {
    $('summary.content-warning-notice').click(toggleDangerousContent);
  };

  window.libreviews.updateContentClickHandlers();

  // Add autocompletion to search box if present
  if ($('#search-input').length)
    setupSearch();

  // For content which is hidden by default and can be expanded, e.g., spoiler
  // warnings, NSFW warnings
  function toggleDangerousContent(event) {
    if ($(this).parent().is('[open]')) {
      $(this).next('.dangerous-content').slideUp(200, () => {
        $(this).parent().removeAttr('open');
      });
    } else {
      $(this).parent().attr('open', '');
      $(this).next('.dangerous-content').slideDown(200);
    }
    event.preventDefault();
  }

  function setupSearch() {
    let ac = new AC($('#search-input')[0], null, queryFn, null, null, triggerFn);
    ac.secondaryTextKey = 'language';

    function triggerFn(row) {
      if (row.urlID)
        window.location = `/${row.urlID}`;
    }

    function queryFn(query) {
      this.results = [];
      query = query.trim();
      if (query) {
        $
          .get(`/api/suggest/thing/${encodeURIComponent(query)}`)
          .done(res => {
            this.results = [];
            if (res.results) {

              // We don't want to show any suggestion more than once, even
              // if it appears in multiple languages
              let seenIDs = [];

              // Helper function for adding labels to the suggestions array
              let processLabelKey = (labelKey, labelLanguage) => {
                for (let label of res.results[labelKey]) {
                  // Don't include any result more than once
                  if (seenIDs.indexOf(label._id) !== -1)
                    continue;
                  seenIDs.push(label._id);

                  let suggestion = {
                    title: label.text,
                    urlID: label.urlID
                  };
                  if (labelLanguage !== config.language)
                    suggestion.language = labelLanguage;

                  this.results.push(suggestion);
                }
                Reflect.deleteProperty(res.results, labelKey);
              };

              // Process the user's currently selected language before others
              let myLabelKey = `labels-${config.language}`;
              if (Array.isArray(res.results[myLabelKey]) && res.results[myLabelKey].length)
                processLabelKey(myLabelKey, config.language);

              // Process remaining languages
              for (let labelKey in res.results) {
                let labelLanguage = (labelKey.match(/labels-(.*)/) || [])[1];
                if (!labelLanguage)
                  continue;
                processLabelKey(labelKey, labelLanguage);
              }
            }
            this.render();
          });
      } else {
        this.render();
      }
    }
  }

  function showInputHelp() {
    let id = this.id;
    // Hide all help texts
    $('.help-text').hide();

    // Show help text for active input
    $(`#${id}-help`).show();

    // We want to avoid pushing down the next form field, so we use
    // absolute positioning if available.
    if (this.getBoundingClientRect && $(`label[for=${id}]`)[0]) {
      let posHelp, posLabel;
      posLabel = $(`label[for=${id}]`)[0].getBoundingClientRect();
      posHelp = $(`#${id}-help`)[0].getBoundingClientRect();

      // Recaculate boundaries for all inputs in case fields have been resized,
      // e.g., textarea via resize triangle. We don't want any help text to
      // start before the rightmost one, so that it doesn't bleed into the
      // control below it
      let maxRight;
      $(this)
        .parents('form')
        .find('input,textarea')
        .each(function() {
          let eleRight = this.getBoundingClientRect().right;
          if (maxRight === undefined || maxRight < eleRight)
            maxRight = eleRight;
        });

      if (posHelp.left > posLabel.right && window.innerWidth > posLabel.width + posHelp.width) {
        // Position vertically aligned with the input we're showing help for
        let newTopPos = Math.floor(window.scrollY) + Math.floor(posLabel.top);
        let newLeftPos = maxRight + 5;
        let style = `position:absolute;top:${newTopPos}px;display:inline-block;left:${newLeftPos}px;`;
        $(`#${id}-help`).attr('style', style);
      } else {
        // Reset position
        $(`#${id}-help`).attr('style', 'display:inline-block;');
      }
    }
  }

  function hideInputHelp() {
    let id = this.id;
    // Keep help visible if user is hoving over it, so
    // links remain accessible
    if (!$('.help-text:hover').length)
      $(`#${id}-help`).hide();
  }

}());
<|endoftext|>


<commit_before>import Vue from 'vue'
import Router from 'vue-router'
import slugify from 'slugify'
import { isLoggedIn } from '@/plugins/auth'
import { apiStore } from '@/plugins/store'

Vue.use(Router)

const NavigationAuth = () => import(/* webpackChunkName: "navigationAuth" */ './views/auth/NavigationAuth.vue')
const NavigationDefault = () => import(/* webpackChunkName: "navigationDefault" */ './views/NavigationDefault.vue')
const NavigationCamp = () => import(/* webpackChunkName: "navigationCamp" */ './views/camp/NavigationCamp.vue')

/* istanbul ignore next */
export default new Router({
  mode: 'history',
  base: window.environment.BASE_URL || '/',
  routes: [
    // Dev-Pages:
    {
      path: '/controls',
      name: 'controls',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "register" */ './views/dev/Controls.vue')
      }
    },
    // Prod-Pages:
    {
      path: '/register',
      name: 'register',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "register" */ './views/auth/Register.vue')
      }
    },
    {
      path: '/register-done',
      name: 'register-done',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "register" */ './views/auth/RegisterDone.vue')
      }
    },
    {
      path: '/activate/:userId/:activationKey',
      name: 'activate',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "register" */ './views/auth/Activate.vue')
      },
      props: {
        default: route => {
          return {
            userId: route.params.userId,
            activationKey: route.params.activationKey
          }
        }
      }
    },
    {
      path: '/login',
      name: 'login',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/auth/Login.vue')
      }
    },
    {
      path: '/loginCallback',
      name: 'loginCallback',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/auth/LoginCallback.vue')
      }
    },
    {
      path: '/',
      name: 'home',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "about" */ './views/Home.vue')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/profile',
      name: 'profile',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "about" */ './views/Profile.vue')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps',
      name: 'camps',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "camps" */ './views/Camps.vue')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps/create',
      name: 'camps/create',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "camps" */ './views/CampCreate.vue')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps/invitation/:inviteKey',
      name: 'campInvitation',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/camp/Invitation.vue')
      },
      props: {
        default: route => {
          return {
            invitation: invitationFromInviteKey(route.params.inviteKey)
          }
        }
      }
    },
    {
      path: '/camps/invitation/rejected',
      name: 'invitationRejected',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/camp/InvitationRejected.vue')
      }
    },
    {
      path: '/camps/invitation/updateError',
      name: 'invitationUpdateError',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/camp/InvitationUpdateError.vue')
      }
    },
    {
      path: '/camps/:campId/:campTitle?',
      components: {
        navigation: NavigationCamp,
        default: () => import(/* webpackChunkName: "camp" */ './views/camp/Camp.vue'),
        aside: () => import(/* webpackChunkName: "periods" */ './views/camp/SideBarPeriods.vue')
      },
      beforeEnter: all([requireAuth, requireCamp]),
      props: {
        navigation: route => ({ camp: campFromRoute(route) }),
        default: route => ({ camp: campFromRoute(route), period: periodFromRoute(route) }),
        aside: route => ({ camp: campFromRoute(route), period: periodFromRoute(route) })
      },
      children: [
        {
          path: 'collaborators',
          name: 'camp/collaborators',
          component: () => import(/* webpackChunkName: "campCollaborators" */ './views/camp/Collaborators.vue')
        },
        {
          path: 'admin',
          name: 'camp/admin',
          component: () => import(/* webpackChunkName: "campAdmin" */ './views/camp/Admin.vue')
        },
        {
          path: 'period/:periodId/:periodTitle?',
          name: 'camp/period',
          component: () => import(/* webpackChunkName: "campProgram" */ './views/camp/CampProgram.vue'),
          beforeEnter: requirePeriod
        },
        {
          path: 'print',
          component: () => import(/* webpackChunkName: "campPrint" */ './views/camp/Print.vue'),
          children: [
            {
              path: '',
              name: 'camp/print',
              component: () => import(/* webpackChunkName: "printCamp" */ './views/camp/PrintCamp.vue'),
              props: route => ({ camp: campFromRoute(route) })
            },
            {
              path: 'activity/:scheduleEntryId/:activityName?',
              name: 'camp/print/activity',
              component: () => import(/* webpackChunkName: "printActivity" */ './views/camp/PrintActivity.vue'),
              props: route => ({ scheduleEntry: scheduleEntryFromRoute(route) })
            },
            {
              path: 'period/:periodId/:periodTitle?',
              name: 'camp/print/period',
              component: () => import(/* webpackChunkName: "printActivity" */ './views/camp/PrintPeriod.vue'),
              props: route => ({ period: periodFromRoute(route) })
            }
          ]
        },
        {
          path: 'story',
          name: 'camp/story',
          component: () => import(/* webpackChunkName: "campPrint" */ './views/camp/Story.vue')
        },
        {
          path: 'material',
          name: 'camp/material',
          component: () => import(/* webpackChunkName: "campMaterial" */ './views/camp/Material.vue')
        },
        {
          path: 'program',
          name: 'camp/program',
          async beforeEnter (to, from, next) {
            const period = await firstFuturePeriod(to)
            if (period) {
              await period.camp()._meta.load
              next(periodRoute(period, to.query))
            } else {
              const camp = await apiStore.get().camps({ campId: to.params.campId })
              next(campRoute(camp, 'admin', to.query))
            }
          }
        },
        {
          path: '',
          name: 'camp/dashboard',
          component: () => import(/* webpackChungName: "camp" */ './views/camp/Dashboard.vue')
        }
      ]
    },
    {
      path: '/camps/:campId/:campTitle/category/:categoryId/:categoryName?',
      name: 'category',
      components: {
        navigation: NavigationCamp,
        default: () => import(/* webpackChunkName: "campCategory" */ './views/activity/Category.vue'),
        aside: () => import(/* webpackChunkName: "periods" */ './views/camp/SideBarPeriods.vue')
      },
      beforeEnter: requireAuth,
      props: {
        navigation: route => ({ camp: campFromRoute(route) }),
        default: route => ({ category: categoryFromRoute(route) }),
        aside: route => ({ camp: campFromRoute(route), period: () => null })
      }
    },
    {
      path: '/camps/:campId/:campTitle/activities/:scheduleEntryId/:activityName?',
      name: 'activity',
      components: {
        navigation: NavigationCamp,
        default: () => import(/* webpackChunkName: "activity" */ './views/activity/Activity.vue'),
        aside: () => import(/* webpackChunkName: "day" */ './views/activity/SideBarProgram.vue')
      },
      beforeEnter: requireAuth,
      props: {
        navigation: route => ({ camp: campFromRoute(route) }),
        default: route => ({ scheduleEntry: scheduleEntryFromRoute(route) }),
        aside: route => ({ day: dayFromScheduleEntryInRoute(route) })
      }
    }
  ]
})

function evaluateGuards (guards, to, from, next) {
  const guardsLeft = guards.slice(0)
  const nextGuard = guardsLeft.shift()

  if (nextGuard === undefined) {
    next()
    return
  }

  nextGuard(to, from, nextArg => {
    if (nextArg === undefined) {
      evaluateGuards(guardsLeft, to, from, next)
      return
    }
    next(nextArg)
  })
}

function all (guards) {
  return (to, from, next) => evaluateGuards(guards, to, from, next)
}

function requireAuth (to, from, next) {
  if (isLoggedIn()) {
    next()
  } else {
    next({ name: 'login', query: to.path === '/' ? {} : { redirect: to.fullPath } })
  }
}

async function requireCamp (to, from, next) {
  await campFromRoute(to).call({ api: { get: apiStore.get } })._meta.load.then(() => {
    next({ query: to.query })
  }).catch(() => {
    next({ name: 'home' })
  })
}

async function requirePeriod (to, from, next) {
  await periodFromRoute(to).call({ api: { get: apiStore.get } })._meta.load.then(() => {
    next()
  }).catch(() => {
    next(campRoute(campFromRoute(to).call({ api: { get: apiStore.get } })))
  })
}

export function campFromRoute (route) {
  return function () {
    // TODO add another decorator for EntrypointNormalizer and add our templated URIs there
    return this.api.get().camps({ id: route.params.campId })
  }
}

export function invitationFromInviteKey (inviteKey) {
  return function () {
    return this.api.get().invitation({ action: 'find', inviteKey: inviteKey })
  }
}

export function periodFromRoute (route) {
  return function () {
    return this.api.get().periods({ id: route.params.periodId })
  }
}

function scheduleEntryFromRoute (route) {
  return function () {
    return this.api.get().scheduleEntries({ id: route.params.scheduleEntryId })
  }
}

function categoryFromRoute (route) {
  return function () {
    const camp = this.api.get().camps({ id: route.params.campId })
    return camp.categories().items.find(c => c.id === route.params.categoryId)
  }
}

function dayFromScheduleEntryInRoute (route) {
  return function () {
    return this.api.get().scheduleEntries({ id: route.params.scheduleEntryId }).day()
  }
}

export function campRoute (camp, subroute = 'dashboard', query = {}) {
  if (camp._meta.loading) return {}
  return { name: 'camp/' + subroute, params: { id: camp.id, campTitle: slugify(camp.title) }, query }
}

export function loginRoute (redirectTo) {
  return { path: '/login', query: { redirect: redirectTo } }
}

export function periodRoute (period, query = {}) {
  const camp = period.camp()
  if (camp._meta.loading || period._meta.loading) return {}
  return {
    name: 'camp/period',
    params: {
      campId: camp.id,
      campTitle: slugify(camp.title),
      periodId: period.id,
      periodTitle: slugify(period.description)
    },
    query
  }
}

export function scheduleEntryRoute (scheduleEntry, query = {}) {
  if (scheduleEntry._meta.loading || scheduleEntry.activity()._meta.loading) return {}

  const camp = scheduleEntry.activity().camp()

  // if (camp._meta.loading) return {}

  return {
    name: 'activity',
    params: {
      campId: camp.id,
      campTitle: slugify(camp.title),
      scheduleEntryId: scheduleEntry.id,
      activityName: slugify(scheduleEntry.activity().title)
    },
    query
  }
}

export function categoryRoute (camp, category, query = {}) {
  if (camp._meta.loading || category._meta.loading) return {}
  return {
    name: 'category',
    params: {
      campId: camp.id,
      campTitle: slugify(camp.title),
      categoryId: category.id,
      categoryName: slugify(category.name)
    },
    query
  }
}

async function firstFuturePeriod (route) {
  const periods = await apiStore.get().camps({ id: route.params.campId }).periods()._meta.load
  // Return the first period that hasn't ended, or if no such period exists, return the first period
  return periods.items.find(period => new Date(period.end) >= new Date()) || periods.items.find(_ => true)
}
<commit_msg>router.js: fix generationof campRoute<commit_after>import Vue from 'vue'
import Router from 'vue-router'
import slugify from 'slugify'
import { isLoggedIn } from '@/plugins/auth'
import { apiStore } from '@/plugins/store'

Vue.use(Router)

const NavigationAuth = () => import(/* webpackChunkName: "navigationAuth" */ './views/auth/NavigationAuth.vue')
const NavigationDefault = () => import(/* webpackChunkName: "navigationDefault" */ './views/NavigationDefault.vue')
const NavigationCamp = () => import(/* webpackChunkName: "navigationCamp" */ './views/camp/NavigationCamp.vue')

/* istanbul ignore next */
export default new Router({
  mode: 'history',
  base: window.environment.BASE_URL || '/',
  routes: [
    // Dev-Pages:
    {
      path: '/controls',
      name: 'controls',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "register" */ './views/dev/Controls.vue')
      }
    },
    // Prod-Pages:
    {
      path: '/register',
      name: 'register',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "register" */ './views/auth/Register.vue')
      }
    },
    {
      path: '/register-done',
      name: 'register-done',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "register" */ './views/auth/RegisterDone.vue')
      }
    },
    {
      path: '/activate/:userId/:activationKey',
      name: 'activate',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "register" */ './views/auth/Activate.vue')
      },
      props: {
        default: route => {
          return {
            userId: route.params.userId,
            activationKey: route.params.activationKey
          }
        }
      }
    },
    {
      path: '/login',
      name: 'login',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/auth/Login.vue')
      }
    },
    {
      path: '/loginCallback',
      name: 'loginCallback',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/auth/LoginCallback.vue')
      }
    },
    {
      path: '/',
      name: 'home',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "about" */ './views/Home.vue')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/profile',
      name: 'profile',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "about" */ './views/Profile.vue')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps',
      name: 'camps',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "camps" */ './views/Camps.vue')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps/create',
      name: 'camps/create',
      components: {
        navigation: NavigationDefault,
        default: () => import(/* webpackChunkName: "camps" */ './views/CampCreate.vue')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps/invitation/:inviteKey',
      name: 'campInvitation',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/camp/Invitation.vue')
      },
      props: {
        default: route => {
          return {
            invitation: invitationFromInviteKey(route.params.inviteKey)
          }
        }
      }
    },
    {
      path: '/camps/invitation/rejected',
      name: 'invitationRejected',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/camp/InvitationRejected.vue')
      }
    },
    {
      path: '/camps/invitation/updateError',
      name: 'invitationUpdateError',
      components: {
        navigation: NavigationAuth,
        default: () => import(/* webpackChunkName: "login" */ './views/camp/InvitationUpdateError.vue')
      }
    },
    {
      path: '/camps/:campId/:campTitle?',
      components: {
        navigation: NavigationCamp,
        default: () => import(/* webpackChunkName: "camp" */ './views/camp/Camp.vue'),
        aside: () => import(/* webpackChunkName: "periods" */ './views/camp/SideBarPeriods.vue')
      },
      beforeEnter: all([requireAuth, requireCamp]),
      props: {
        navigation: route => ({ camp: campFromRoute(route) }),
        default: route => ({ camp: campFromRoute(route), period: periodFromRoute(route) }),
        aside: route => ({ camp: campFromRoute(route), period: periodFromRoute(route) })
      },
      children: [
        {
          path: 'collaborators',
          name: 'camp/collaborators',
          component: () => import(/* webpackChunkName: "campCollaborators" */ './views/camp/Collaborators.vue')
        },
        {
          path: 'admin',
          name: 'camp/admin',
          component: () => import(/* webpackChunkName: "campAdmin" */ './views/camp/Admin.vue')
        },
        {
          path: 'period/:periodId/:periodTitle?',
          name: 'camp/period',
          component: () => import(/* webpackChunkName: "campProgram" */ './views/camp/CampProgram.vue'),
          beforeEnter: requirePeriod
        },
        {
          path: 'print',
          component: () => import(/* webpackChunkName: "campPrint" */ './views/camp/Print.vue'),
          children: [
            {
              path: '',
              name: 'camp/print',
              component: () => import(/* webpackChunkName: "printCamp" */ './views/camp/PrintCamp.vue'),
              props: route => ({ camp: campFromRoute(route) })
            },
            {
              path: 'activity/:scheduleEntryId/:activityName?',
              name: 'camp/print/activity',
              component: () => import(/* webpackChunkName: "printActivity" */ './views/camp/PrintActivity.vue'),
              props: route => ({ scheduleEntry: scheduleEntryFromRoute(route) })
            },
            {
              path: 'period/:periodId/:periodTitle?',
              name: 'camp/print/period',
              component: () => import(/* webpackChunkName: "printActivity" */ './views/camp/PrintPeriod.vue'),
              props: route => ({ period: periodFromRoute(route) })
            }
          ]
        },
        {
          path: 'story',
          name: 'camp/story',
          component: () => import(/* webpackChunkName: "campPrint" */ './views/camp/Story.vue')
        },
        {
          path: 'material',
          name: 'camp/material',
          component: () => import(/* webpackChunkName: "campMaterial" */ './views/camp/Material.vue')
        },
        {
          path: 'program',
          name: 'camp/program',
          async beforeEnter (to, from, next) {
            const period = await firstFuturePeriod(to)
            if (period) {
              await period.camp()._meta.load
              next(periodRoute(period, to.query))
            } else {
              const camp = await apiStore.get().camps({ campId: to.params.campId })
              next(campRoute(camp, 'admin', to.query))
            }
          }
        },
        {
          path: '',
          name: 'camp/dashboard',
          component: () => import(/* webpackChungName: "camp" */ './views/camp/Dashboard.vue')
        }
      ]
    },
    {
      path: '/camps/:campId/:campTitle/category/:categoryId/:categoryName?',
      name: 'category',
      components: {
        navigation: NavigationCamp,
        default: () => import(/* webpackChunkName: "campCategory" */ './views/activity/Category.vue'),
        aside: () => import(/* webpackChunkName: "periods" */ './views/camp/SideBarPeriods.vue')
      },
      beforeEnter: requireAuth,
      props: {
        navigation: route => ({ camp: campFromRoute(route) }),
        default: route => ({ category: categoryFromRoute(route) }),
        aside: route => ({ camp: campFromRoute(route), period: () => null })
      }
    },
    {
      path: '/camps/:campId/:campTitle/activities/:scheduleEntryId/:activityName?',
      name: 'activity',
      components: {
        navigation: NavigationCamp,
        default: () => import(/* webpackChunkName: "activity" */ './views/activity/Activity.vue'),
        aside: () => import(/* webpackChunkName: "day" */ './views/activity/SideBarProgram.vue')
      },
      beforeEnter: requireAuth,
      props: {
        navigation: route => ({ camp: campFromRoute(route) }),
        default: route => ({ scheduleEntry: scheduleEntryFromRoute(route) }),
        aside: route => ({ day: dayFromScheduleEntryInRoute(route) })
      }
    }
  ]
})

function evaluateGuards (guards, to, from, next) {
  const guardsLeft = guards.slice(0)
  const nextGuard = guardsLeft.shift()

  if (nextGuard === undefined) {
    next()
    return
  }

  nextGuard(to, from, nextArg => {
    if (nextArg === undefined) {
      evaluateGuards(guardsLeft, to, from, next)
      return
    }
    next(nextArg)
  })
}

function all (guards) {
  return (to, from, next) => evaluateGuards(guards, to, from, next)
}

function requireAuth (to, from, next) {
  if (isLoggedIn()) {
    next()
  } else {
    next({ name: 'login', query: to.path === '/' ? {} : { redirect: to.fullPath } })
  }
}

async function requireCamp (to, from, next) {
  await campFromRoute(to).call({ api: { get: apiStore.get } })._meta.load.then(() => {
    next({ query: to.query })
  }).catch(() => {
    next({ name: 'home' })
  })
}

async function requirePeriod (to, from, next) {
  await periodFromRoute(to).call({ api: { get: apiStore.get } })._meta.load.then(() => {
    next()
  }).catch(() => {
    next(campRoute(campFromRoute(to).call({ api: { get: apiStore.get } })))
  })
}

export function campFromRoute (route) {
  return function () {
    // TODO add another decorator for EntrypointNormalizer and add our templated URIs there
    return this.api.get().camps({ id: route.params.campId })
  }
}

export function invitationFromInviteKey (inviteKey) {
  return function () {
    return this.api.get().invitation({ action: 'find', inviteKey: inviteKey })
  }
}

export function periodFromRoute (route) {
  return function () {
    return this.api.get().periods({ id: route.params.periodId })
  }
}

function scheduleEntryFromRoute (route) {
  return function () {
    return this.api.get().scheduleEntries({ id: route.params.scheduleEntryId })
  }
}

function categoryFromRoute (route) {
  return function () {
    const camp = this.api.get().camps({ id: route.params.campId })
    return camp.categories().items.find(c => c.id === route.params.categoryId)
  }
}

function dayFromScheduleEntryInRoute (route) {
  return function () {
    return this.api.get().scheduleEntries({ id: route.params.scheduleEntryId }).day()
  }
}

export function campRoute (camp, subroute = 'dashboard', query = {}) {
  if (camp._meta.loading) return {}
  return { name: 'camp/' + subroute, params: { campId: camp.id, campTitle: slugify(camp.title) }, query }
}

export function loginRoute (redirectTo) {
  return { path: '/login', query: { redirect: redirectTo } }
}

export function periodRoute (period, query = {}) {
  const camp = period.camp()
  if (camp._meta.loading || period._meta.loading) return {}
  return {
    name: 'camp/period',
    params: {
      campId: camp.id,
      campTitle: slugify(camp.title),
      periodId: period.id,
      periodTitle: slugify(period.description)
    },
    query
  }
}

export function scheduleEntryRoute (scheduleEntry, query = {}) {
  if (scheduleEntry._meta.loading || scheduleEntry.activity()._meta.loading) return {}

  const camp = scheduleEntry.activity().camp()

  // if (camp._meta.loading) return {}

  return {
    name: 'activity',
    params: {
      campId: camp.id,
      campTitle: slugify(camp.title),
      scheduleEntryId: scheduleEntry.id,
      activityName: slugify(scheduleEntry.activity().title)
    },
    query
  }
}

export function categoryRoute (camp, category, query = {}) {
  if (camp._meta.loading || category._meta.loading) return {}
  return {
    name: 'category',
    params: {
      campId: camp.id,
      campTitle: slugify(camp.title),
      categoryId: category.id,
      categoryName: slugify(category.name)
    },
    query
  }
}

async function firstFuturePeriod (route) {
  const periods = await apiStore.get().camps({ id: route.params.campId }).periods()._meta.load
  // Return the first period that hasn't ended, or if no such period exists, return the first period
  return periods.items.find(period => new Date(period.end) >= new Date()) || periods.items.find(_ => true)
}
<|endoftext|>


<commit_before>goog.provide('ol.MapBrowserEvent');
goog.provide('ol.MapBrowserEvent.EventType');
goog.provide('ol.MapBrowserEventHandler');

goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('ol.BrowserFeature');
goog.require('ol.Coordinate');
goog.require('ol.FrameState');
goog.require('ol.MapEvent');
goog.require('ol.Pixel');

/**
 * @constructor
 * @extends {ol.MapEvent}
 * @param {string} type Event type.
 * @param {ol.Map} map Map.
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @param {?ol.FrameState=} opt_frameState Frame state.
 */
ol.MapBrowserEvent = function(type, map, browserEvent, opt_frameState) {

  goog.base(this, type, map, opt_frameState);

  /**
   * @type {goog.events.BrowserEvent}
   */
  this.browserEvent = browserEvent;

  /**
   * @private
   * @type {ol.Coordinate}
   */
  this.coordinate_ = null;

  /**
   * @private
   * @type {ol.Pixel}
   */
  this.pixel_ = null;

};
goog.inherits(ol.MapBrowserEvent, ol.MapEvent);

/**
 * @return {ol.Coordinate} Coordinate.
 */
ol.MapBrowserEvent.prototype.getCoordinate = function() {
  if (goog.isNull(this.coordinate_)) {
    this.coordinate_ = this.map.getEventCoordinate(
        this.browserEvent.getBrowserEvent());
  }
  return this.coordinate_;
};

/**
 * Get pixel offset of the event from the top-left corner of the map viewport.
 * @return {ol.Pixel} Pixel offset.
 */
ol.MapBrowserEvent.prototype.getPixel = function() {
  if (goog.isNull(this.pixel_)) {
    this.pixel_ = this.map.getEventPixel(this.browserEvent.getBrowserEvent());
  }
  return this.pixel_;
};

/**
 * @return {boolean} Do we have a left click?
 */
ol.MapBrowserEvent.prototype.isMouseActionButton = function() {
  // always assume a left-click on touch devices
  return ol.BrowserFeature.HAS_TOUCH ||
      this.browserEvent.isMouseActionButton();
};

/**
 * Prevents the default browser action.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault
 * @override
 */
ol.MapBrowserEvent.prototype.preventDefault = function() {
  goog.base(this, 'preventDefault');
  this.browserEvent.preventDefault();
};

/**
 * Prevents further propagation of the current event.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
 * @override
 */
ol.MapBrowserEvent.prototype.stopPropagation = function() {
  goog.base(this, 'stopPropagation');
  this.browserEvent.stopPropagation();
};

/**
 * @param {ol.Map} map The map with the viewport to listen to events on.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
ol.MapBrowserEventHandler = function(map) {

  goog.base(this);

  /**
   * This is the element that we will listen to the real events on.
   * @type {ol.Map}
   * @private
   */
  this.map_ = map;

  /**
   * @type {boolean}
   * @private
   */
  this.dragged_ = false;

  /**
   * Timestamp for the first click of a double click. Will be set back to 0
   * as soon as a double click is detected.
   * @type {?number}
   * @private
   */
  this.timestamp_ = null;

  /**
   * @type {Array.<number>}
   * @private
   */
  this.listenerKeys_ = null;

  /**
   * @type {Array.<number>}
   * @private
   */
  this.dragListenerKeys_ = null;

  /**
   * @type {Array.<number>}
   * @private
   */
  this.touchListenerKeys_ = null;

  /**
   * @type {goog.events.BrowserEvent}
   * @private
   */
  this.down_ = null;

  var element = this.map_.getViewport();
  this.listenerKeys_ = [
    goog.events.listen(element,
        [goog.events.EventType.CLICK, goog.events.EventType.DBLCLICK],
        this.click_, false, this),
    goog.events.listen(element,
        goog.events.EventType.MOUSEDOWN,
        this.handleMouseDown_, false, this),
    goog.events.listen(element,
        goog.events.EventType.MOUSEOUT,
        this.relayEvent_, false, this)
  ];
  // touch events
  this.touchListenerKeys_ = [
    goog.events.listen(element, [
      goog.events.EventType.TOUCHSTART,
      goog.events.EventType.MSPOINTERDOWN
    ], this.handleTouchStart_, false, this),
    goog.events.listen(goog.global.document, [
      goog.events.EventType.TOUCHMOVE,
      goog.events.EventType.MSPOINTERMOVE
    ], this.handleTouchMove_, false, this),
    goog.events.listen(goog.global.document, [
      goog.events.EventType.TOUCHEND,
      goog.events.EventType.MSPOINTERUP
    ], this.handleTouchEnd_, false, this)
  ];

};
goog.inherits(ol.MapBrowserEventHandler, goog.events.EventTarget);

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.click_ = function(browserEvent) {
  if (!this.dragged_) {
    var newEvent;
    var type = browserEvent.type;
    if (this.timestamp_ === 0 || type == goog.events.EventType.DBLCLICK) {
      newEvent = new ol.MapBrowserEvent(
          ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, browserEvent);
      this.dispatchEvent(newEvent);
    } else {
      newEvent = new ol.MapBrowserEvent(
          ol.MapBrowserEvent.EventType.CLICK, this.map_, browserEvent);
      this.dispatchEvent(newEvent);
    }
  }
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleMouseUp_ = function(browserEvent) {
  if (this.down_) {
    this.down_ = null;
    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    this.dragListenerKeys_ = null;
    if (this.dragged_) {
      var newEvent = new ol.MapBrowserEvent(
          ol.MapBrowserEvent.EventType.DRAGEND, this.map_, browserEvent);
      this.dispatchEvent(newEvent);
    }
  }
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleMouseDown_ = function(browserEvent) {
  var newEvent = new ol.MapBrowserEvent(
      ol.MapBrowserEvent.EventType.DOWN, this.map_, browserEvent);
  this.dispatchEvent(newEvent);
  if (!this.down_) {
    this.down_ = browserEvent;
    this.dragged_ = false;
    this.dragListenerKeys_ = [
      goog.events.listen(goog.global.document, goog.events.EventType.MOUSEMOVE,
          this.handleMouseMove_, false, this),
      goog.events.listen(goog.global.document, goog.events.EventType.MOUSEUP,
          this.handleMouseUp_, false, this)
    ];
    // prevent browser image dragging with the dom renderer
    browserEvent.preventDefault();
  }
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleMouseMove_ = function(browserEvent) {
  var newEvent;
  if (!this.dragged_) {
    this.dragged_ = true;
    newEvent = new ol.MapBrowserEvent(
        ol.MapBrowserEvent.EventType.DRAGSTART, this.map_, this.down_);
    this.dispatchEvent(newEvent);
  }
  newEvent = new ol.MapBrowserEvent(
      ol.MapBrowserEvent.EventType.DRAG, this.map_, browserEvent);
  this.dispatchEvent(newEvent);
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.relayEvent_ = function(browserEvent) {
  this.dispatchEvent(new ol.MapBrowserEvent(
      browserEvent.type, this.map_, browserEvent));
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleTouchStart_ = function(browserEvent) {
  // prevent context menu
  // When the IE pointer events are used, this prevents a
  // 'mousedown' from being fired after this event for the primary
  // contact (first finger on the screen or mouse)
  browserEvent.preventDefault();
  this.down_ = browserEvent;
  this.dragged_ = false;
  var newEvent = new ol.MapBrowserEvent(
      ol.MapBrowserEvent.EventType.TOUCHSTART, this.map_, browserEvent);
  this.dispatchEvent(newEvent);
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleTouchMove_ = function(browserEvent) {
  if (this.down_) {
    // 'touchmove' events are dispatched only when this.down_ is set
    // (set after a touch start) to prevent unwanted events when the
    // mouse hover the page. This only happens with the IE pointer
    // event system.
    this.dragged_ = true;
    var newEvent = new ol.MapBrowserEvent(
        ol.MapBrowserEvent.EventType.TOUCHMOVE, this.map_, browserEvent);
    this.dispatchEvent(newEvent);
  }
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleTouchEnd_ = function(browserEvent) {
  var newEvent = new ol.MapBrowserEvent(
      ol.MapBrowserEvent.EventType.TOUCHEND, this.map_, browserEvent);
  this.dispatchEvent(newEvent);
  if (!this.dragged_) {
    var now = goog.now();
    if (!this.timestamp_ || now - this.timestamp_ > 250) {
      this.timestamp_ = now;
    } else {
      this.timestamp_ = 0;
    }
    if (!goog.isNull(this.down_)) {
      this.click_(this.down_);
    }
  }
  this.down_ = null;
};

/**
 * FIXME empty description for jsdoc
 */
ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
  if (!goog.isNull(this.listenerKeys_)) {
    goog.array.forEach(this.listenerKeys_, goog.events.unlistenByKey);
    this.listenerKeys_ = null;
  }
  if (!goog.isNull(this.dragListenerKeys_)) {
    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    this.dragListenerKeys_ = null;
  }
  if (!goog.isNull(this.touchListenerKeys_)) {
    goog.array.forEach(this.touchListenerKeys_, goog.events.unlistenByKey);
    this.touchListenerKeys_ = null;
  }
  goog.base(this, 'disposeInternal');
};

/**
 * Constants for event names.
 * @enum {string}
 */
ol.MapBrowserEvent.EventType = {
  CLICK: goog.events.EventType.CLICK,
  DBLCLICK: goog.events.EventType.DBLCLICK,
  DOWN: 'down',
  DRAGSTART: 'dragstart',
  DRAG: 'drag',
  DRAGEND: 'dragend',
  TOUCHSTART: goog.events.EventType.TOUCHSTART,
  TOUCHMOVE: goog.events.EventType.TOUCHMOVE,
  TOUCHEND: goog.events.EventType.TOUCHEND,
  MOUSEOUT: goog.events.EventType.MOUSEOUT
};
<commit_msg>Emulate click and dblclick on touch devices<commit_after>goog.provide('ol.MapBrowserEvent');
goog.provide('ol.MapBrowserEvent.EventType');
goog.provide('ol.MapBrowserEventHandler');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('ol.BrowserFeature');
goog.require('ol.Coordinate');
goog.require('ol.FrameState');
goog.require('ol.MapEvent');
goog.require('ol.Pixel');

/**
 * @constructor
 * @extends {ol.MapEvent}
 * @param {string} type Event type.
 * @param {ol.Map} map Map.
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @param {?ol.FrameState=} opt_frameState Frame state.
 */
ol.MapBrowserEvent = function(type, map, browserEvent, opt_frameState) {

  goog.base(this, type, map, opt_frameState);

  /**
   * @type {goog.events.BrowserEvent}
   */
  this.browserEvent = browserEvent;

  /**
   * @private
   * @type {ol.Coordinate}
   */
  this.coordinate_ = null;

  /**
   * @private
   * @type {ol.Pixel}
   */
  this.pixel_ = null;

};
goog.inherits(ol.MapBrowserEvent, ol.MapEvent);

/**
 * @return {ol.Coordinate} Coordinate.
 */
ol.MapBrowserEvent.prototype.getCoordinate = function() {
  if (goog.isNull(this.coordinate_)) {
    this.coordinate_ = this.map.getEventCoordinate(
        this.browserEvent.getBrowserEvent());
  }
  return this.coordinate_;
};

/**
 * Get pixel offset of the event from the top-left corner of the map viewport.
 * @return {ol.Pixel} Pixel offset.
 */
ol.MapBrowserEvent.prototype.getPixel = function() {
  if (goog.isNull(this.pixel_)) {
    this.pixel_ = this.map.getEventPixel(this.browserEvent.getBrowserEvent());
  }
  return this.pixel_;
};

/**
 * @return {boolean} Do we have a left click?
 */
ol.MapBrowserEvent.prototype.isMouseActionButton = function() {
  // always assume a left-click on touch devices
  return ol.BrowserFeature.HAS_TOUCH ||
      this.browserEvent.isMouseActionButton();
};

/**
 * Prevents the default browser action.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault
 * @override
 */
ol.MapBrowserEvent.prototype.preventDefault = function() {
  goog.base(this, 'preventDefault');
  this.browserEvent.preventDefault();
};

/**
 * Prevents further propagation of the current event.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
 * @override
 */
ol.MapBrowserEvent.prototype.stopPropagation = function() {
  goog.base(this, 'stopPropagation');
  this.browserEvent.stopPropagation();
};

/**
 * @param {ol.Map} map The map with the viewport to listen to events on.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
ol.MapBrowserEventHandler = function(map) {

  goog.base(this);

  /**
   * This is the element that we will listen to the real events on.
   * @type {ol.Map}
   * @private
   */
  this.map_ = map;

  /**
   * @type {number}
   * @private
   */
  this.clickTimeoutId_ = 0;

  /**
   * @type {boolean}
   * @private
   */
  this.dragged_ = false;

  /**
   * @type {Array.<number>}
   * @private
   */
  this.listenerKeys_ = null;

  /**
   * @type {Array.<number>}
   * @private
   */
  this.dragListenerKeys_ = null;

  /**
   * @type {Array.<number>}
   * @private
   */
  this.touchListenerKeys_ = null;

  /**
   * @type {goog.events.BrowserEvent}
   * @private
   */
  this.down_ = null;

  var element = this.map_.getViewport();
  this.listenerKeys_ = [
    goog.events.listen(element,
        [goog.events.EventType.CLICK, goog.events.EventType.DBLCLICK],
        this.click_, false, this),
    goog.events.listen(element,
        goog.events.EventType.MOUSEDOWN,
        this.handleMouseDown_, false, this),
    goog.events.listen(element,
        goog.events.EventType.MOUSEOUT,
        this.relayEvent_, false, this)
  ];
  // touch events
  this.touchListenerKeys_ = [
    goog.events.listen(element, [
      goog.events.EventType.TOUCHSTART,
      goog.events.EventType.MSPOINTERDOWN
    ], this.handleTouchStart_, false, this),
    goog.events.listen(goog.global.document, [
      goog.events.EventType.TOUCHMOVE,
      goog.events.EventType.MSPOINTERMOVE
    ], this.handleTouchMove_, false, this),
    goog.events.listen(goog.global.document, [
      goog.events.EventType.TOUCHEND,
      goog.events.EventType.MSPOINTERUP
    ], this.handleTouchEnd_, false, this)
  ];

};
goog.inherits(ol.MapBrowserEventHandler, goog.events.EventTarget);

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.click_ = function(browserEvent) {
  if (!this.dragged_) {
    var newEvent;
    var type = browserEvent.type;
    if (type == goog.events.EventType.DBLCLICK) {
      newEvent = new ol.MapBrowserEvent(
          ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, browserEvent);
      this.dispatchEvent(newEvent);
    } else {
      newEvent = new ol.MapBrowserEvent(
          ol.MapBrowserEvent.EventType.CLICK, this.map_, browserEvent);
      this.dispatchEvent(newEvent);
    }
  }
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleMouseUp_ = function(browserEvent) {
  if (this.down_) {
    this.down_ = null;
    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    this.dragListenerKeys_ = null;
    if (this.dragged_) {
      var newEvent = new ol.MapBrowserEvent(
          ol.MapBrowserEvent.EventType.DRAGEND, this.map_, browserEvent);
      this.dispatchEvent(newEvent);
    }
  }
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleMouseDown_ = function(browserEvent) {
  var newEvent = new ol.MapBrowserEvent(
      ol.MapBrowserEvent.EventType.DOWN, this.map_, browserEvent);
  this.dispatchEvent(newEvent);
  if (!this.down_) {
    this.down_ = browserEvent;
    this.dragged_ = false;
    this.dragListenerKeys_ = [
      goog.events.listen(goog.global.document, goog.events.EventType.MOUSEMOVE,
          this.handleMouseMove_, false, this),
      goog.events.listen(goog.global.document, goog.events.EventType.MOUSEUP,
          this.handleMouseUp_, false, this)
    ];
    // prevent browser image dragging with the dom renderer
    browserEvent.preventDefault();
  }
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleMouseMove_ = function(browserEvent) {
  var newEvent;
  if (!this.dragged_) {
    this.dragged_ = true;
    newEvent = new ol.MapBrowserEvent(
        ol.MapBrowserEvent.EventType.DRAGSTART, this.map_, this.down_);
    this.dispatchEvent(newEvent);
  }
  newEvent = new ol.MapBrowserEvent(
      ol.MapBrowserEvent.EventType.DRAG, this.map_, browserEvent);
  this.dispatchEvent(newEvent);
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.relayEvent_ = function(browserEvent) {
  this.dispatchEvent(new ol.MapBrowserEvent(
      browserEvent.type, this.map_, browserEvent));
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleTouchStart_ = function(browserEvent) {
  // prevent context menu
  // When the IE pointer events are used, this prevents a
  // 'mousedown' from being fired after this event for the primary
  // contact (first finger on the screen or mouse)
  browserEvent.preventDefault();
  this.down_ = browserEvent;
  this.dragged_ = false;
  var newEvent = new ol.MapBrowserEvent(
      ol.MapBrowserEvent.EventType.TOUCHSTART, this.map_, browserEvent);
  this.dispatchEvent(newEvent);
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleTouchMove_ = function(browserEvent) {
  if (this.down_) {
    // 'touchmove' events are dispatched only when this.down_ is set
    // (set after a touch start) to prevent unwanted events when the
    // mouse hover the page. This only happens with the IE pointer
    // event system.
    this.dragged_ = true;
    var newEvent = new ol.MapBrowserEvent(
        ol.MapBrowserEvent.EventType.TOUCHMOVE, this.map_, browserEvent);
    this.dispatchEvent(newEvent);
  }
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handleTouchEnd_ = function(browserEvent) {
  var newEvent = new ol.MapBrowserEvent(
      ol.MapBrowserEvent.EventType.TOUCHEND, this.map_, browserEvent);
  this.dispatchEvent(newEvent);
  if (!this.dragged_) {
    goog.asserts.assert(!goog.isNull(this.down_));
    if (this.clickTimeoutId_ !== 0) {
      // double-click
      goog.global.clearTimeout(this.clickTimeoutId_);
      this.clickTimeoutId_ = 0;
      newEvent = new ol.MapBrowserEvent(
          ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, this.down_);
      this.dispatchEvent(newEvent);
      this.down_ = null;
    } else {
      // click
      this.clickTimeoutId_ = goog.global.setTimeout(goog.bind(function() {
        this.clickTimeoutId_ = 0;
        newEvent = new ol.MapBrowserEvent(
            ol.MapBrowserEvent.EventType.CLICK, this.map_, this.down_);
        this.dispatchEvent(newEvent);
        this.down_ = null;
      }, this), 250);
    }
  }
};

/**
 * FIXME empty description for jsdoc
 */
ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
  if (!goog.isNull(this.listenerKeys_)) {
    goog.array.forEach(this.listenerKeys_, goog.events.unlistenByKey);
    this.listenerKeys_ = null;
  }
  if (!goog.isNull(this.dragListenerKeys_)) {
    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    this.dragListenerKeys_ = null;
  }
  if (!goog.isNull(this.touchListenerKeys_)) {
    goog.array.forEach(this.touchListenerKeys_, goog.events.unlistenByKey);
    this.touchListenerKeys_ = null;
  }
  goog.base(this, 'disposeInternal');
};

/**
 * Constants for event names.
 * @enum {string}
 */
ol.MapBrowserEvent.EventType = {
  CLICK: goog.events.EventType.CLICK,
  DBLCLICK: goog.events.EventType.DBLCLICK,
  DOWN: 'down',
  DRAGSTART: 'dragstart',
  DRAG: 'drag',
  DRAGEND: 'dragend',
  TOUCHSTART: goog.events.EventType.TOUCHSTART,
  TOUCHMOVE: goog.events.EventType.TOUCHMOVE,
  TOUCHEND: goog.events.EventType.TOUCHEND,
  MOUSEOUT: goog.events.EventType.MOUSEOUT
};
<|endoftext|>


<commit_before>const _ = require('lodash'),
  fstate = require('../util/fstate'),
  digest = require('../util/digest'),
  hashcache = require('../util/hashcache'),
  log = require('../util/log')
  ;

function buildDigest(buildDir, cb) {
  fstate(buildDir, { includeDirectories: true }, (err, files) => {
    if (err) return cb(err);

    const buildId = digest(_.map(
        _.sortBy(files, 'relPath'),
        fileInfo => hashcache(fileInfo.absPath)).join(' '), 'base64');

    log.verbose(`Current build-digest for ${buildDir} is "${buildId}"`);

    return cb(null, buildId);
  });
}

module.exports = _.debounce(buildDigest, 300);
<commit_msg>Exclude directories from build digest.<commit_after>const _ = require('lodash'),
  fstate = require('../util/fstate'),
  digest = require('../util/digest'),
  hashcache = require('../util/hashcache'),
  log = require('../util/log')
  ;

function buildDigest(buildDir, cb) {
  fstate(buildDir, (err, files) => {
    if (err) return cb(err);

    const buildId = digest(_.map(
        _.sortBy(files, 'relPath'),
        fileInfo => hashcache(fileInfo.absPath)).join(' '), 'base64');

    log.verbose(`Current build-digest for ${buildDir} is "${buildId}"`);

    return cb(null, buildId);
  });
}

module.exports = _.debounce(buildDigest, 300);
<|endoftext|>