<commit_before>/* @flow */

import { decodeHTML } from 'entities'
import { parseHTML } from './html-parser'
import { parseText } from './text-parser'
import { cached, no, camelize } from 'shared/util'
import {
  pluckModuleFunction,
  getAndRemoveAttr,
  addProp,
  addAttr,
  addHandler,
  addDirective,
  getBindingAttr,
  baseWarn
} from '../helpers'

export const dirRE = /^v-|^@|^:/
export const forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/
export const forIteratorRE = /\(([^,]*),([^,]*)(?:,([^,]*))?\)/
const bindRE = /^:|^v-bind:/
const onRE = /^@|^v-on:/
const argRE = /:(.*)$/
const modifierRE = /\.[^\.]+/g
const specialNewlineRE = /\u2028|\u2029/g

const decodeHTMLCached = cached(decodeHTML)

// configurable state
let warn
let platformGetTagNamespace
let platformMustUseProp
let platformIsPreTag
let preTransforms
let transforms
let postTransforms
let delimiters

/**
 * Convert HTML string to AST.
 */
export function parse (
  template: string,
  options: CompilerOptions
): ASTElement | void {
  warn = options.warn || baseWarn
  platformGetTagNamespace = options.getTagNamespace || no
  platformMustUseProp = options.mustUseProp || no
  platformIsPreTag = options.isPreTag || no
  preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
  transforms = pluckModuleFunction(options.modules, 'transformNode')
  postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
  delimiters = options.delimiters
  const stack = []
  const preserveWhitespace = options.preserveWhitespace !== false
  let root
  let currentParent
  let inVPre = false
  let inPre = false
  let warned = false
  parseHTML(template, {
    expectHTML: options.expectHTML,
    isUnaryTag: options.isUnaryTag,
    isFromDOM: options.isFromDOM,
    shouldDecodeTags: options.shouldDecodeTags,
    shouldDecodeNewlines: options.shouldDecodeNewlines,
    start (tag, attrs, unary) {
      // check namespace.
      // inherit parent ns if there is one
      const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)

      // handle IE svg bug
      /* istanbul ignore if */
      if (options.isIE && ns === 'svg') {
        attrs = guardIESVGBug(attrs)
      }

      const element: ASTElement = {
        type: 1,
        tag,
        attrsList: attrs,
        attrsMap: makeAttrsMap(attrs),
        parent: currentParent,
        children: []
      }
      if (ns) {
        element.ns = ns
      }

      if (process.env.VUE_ENV !== 'server' && isForbiddenTag(element)) {
        element.forbidden = true
        process.env.NODE_ENV !== 'production' && warn(
          'Templates should only be responsible for mapping the state to the ' +
          'UI. Avoid placing tags with side-effects in your templates, such as ' +
          `<${tag}>.`
        )
      }

      // apply pre-transforms
      for (let i = 0; i < preTransforms.length; i++) {
        preTransforms[i](element, options)
      }

      if (!inVPre) {
        processPre(element)
        if (element.pre) {
          inVPre = true
        }
      }
      if (platformIsPreTag(element.tag)) {
        inPre = true
      }
      if (inVPre) {
        processRawAttrs(element)
      } else {
        processFor(element)
        processIf(element)
        processOnce(element)
        processKey(element)

        // determine whether this is a plain element after
        // removing structural attributes
        element.plain = !element.key && !attrs.length

        processRef(element)
        processSlot(element)
        processComponent(element)
        for (let i = 0; i < transforms.length; i++) {
          transforms[i](element, options)
        }
        processAttrs(element)
      }

      function checkRootConstraints (el) {
        if (process.env.NODE_ENV !== 'production') {
          if (el.tag === 'slot' || el.tag === 'template') {
            warn(
              `Cannot use <${el.tag}> as component root element because it may ` +
              'contain multiple nodes:\n' + template
            )
          }
          if (el.attrsMap.hasOwnProperty('v-for')) {
            warn(
              'Cannot use v-for on stateful component root element because ' +
              'it renders multiple elements:\n' + template
            )
          }
        }
      }

      // tree management
      if (!root) {
        root = element
        checkRootConstraints(root)
      } else if (process.env.NODE_ENV !== 'production' && !stack.length && !warned) {
        // allow 2 root elements with v-if and v-else
        if (root.if && element.else) {
          checkRootConstraints(element)
          root.elseBlock = element
        } else {
          warned = true
          warn(
            `Component template should contain exactly one root element:\n\n${template}`
          )
        }
      }
      if (currentParent && !element.forbidden) {
        if (element.else) {
          processElse(element, currentParent)
        } else {
          currentParent.children.push(element)
          element.parent = currentParent
        }
      }
      if (!unary) {
        currentParent = element
        stack.push(element)
      }
      // apply post-transforms
      for (let i = 0; i < postTransforms.length; i++) {
        postTransforms[i](element, options)
      }
    },

    end () {
      // remove trailing whitespace
      const element = stack[stack.length - 1]
      const lastNode = element.children[element.children.length - 1]
      if (lastNode && lastNode.type === 3 && lastNode.text === ' ') {
        element.children.pop()
      }
      // pop stack
      stack.length -= 1
      currentParent = stack[stack.length - 1]
      // check pre state
      if (element.pre) {
        inVPre = false
      }
      if (platformIsPreTag(element.tag)) {
        inPre = false
      }
    },

    chars (text: string) {
      if (!currentParent) {
        if (process.env.NODE_ENV !== 'production' && !warned && text === template) {
          warned = true
          warn(
            'Component template requires a root element, rather than just text:\n\n' + template
          )
        }
        return
      }
      text = inPre || text.trim()
        ? decodeHTMLCached(text)
        // only preserve whitespace if its not right after a starting tag
        : preserveWhitespace && currentParent.children.length ? ' ' : ''
      if (text) {
        let expression
        if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) {
          currentParent.children.push({
            type: 2,
            expression,
            text
          })
        } else {
          // #3895 special character
          text = text.replace(specialNewlineRE, '')
          currentParent.children.push({
            type: 3,
            text
          })
        }
      }
    }
  })
  return root
}

function processPre (el) {
  if (getAndRemoveAttr(el, 'v-pre') != null) {
    el.pre = true
  }
}

function processRawAttrs (el) {
  const l = el.attrsList.length
  if (l) {
    const attrs = el.attrs = new Array(l)
    for (let i = 0; i < l; i++) {
      attrs[i] = {
        name: el.attrsList[i].name,
        value: JSON.stringify(el.attrsList[i].value)
      }
    }
  } else if (!el.pre) {
    // non root node in pre blocks with no attributes
    el.plain = true
  }
}

function processKey (el) {
  const exp = getBindingAttr(el, 'key')
  if (exp) {
    if (process.env.NODE_ENV !== 'production' && el.tag === 'template') {
      warn(`<template> cannot be keyed. Place the key on real elements instead.`)
    }
    el.key = exp
  }
}

function processRef (el) {
  const ref = getBindingAttr(el, 'ref')
  if (ref) {
    el.ref = ref
    el.refInFor = checkInFor(el)
  }
}

function processFor (el) {
  let exp
  if ((exp = getAndRemoveAttr(el, 'v-for'))) {
    const inMatch = exp.match(forAliasRE)
    if (!inMatch) {
      process.env.NODE_ENV !== 'production' && warn(
        `Invalid v-for expression: ${exp}`
      )
      return
    }
    el.for = inMatch[2].trim()
    const alias = inMatch[1].trim()
    const iteratorMatch = alias.match(forIteratorRE)
    if (iteratorMatch) {
      el.alias = iteratorMatch[1].trim()
      el.iterator1 = iteratorMatch[2].trim()
      if (iteratorMatch[3]) {
        el.iterator2 = iteratorMatch[3].trim()
      }
    } else {
      el.alias = alias
    }
  }
}

function processIf (el) {
  const exp = getAndRemoveAttr(el, 'v-if')
  if (exp) {
    el.if = exp
  }
  if (getAndRemoveAttr(el, 'v-else') != null) {
    el.else = true
  }
}

function processElse (el, parent) {
  const prev = findPrevElement(parent.children)
  if (prev && prev.if) {
    prev.elseBlock = el
  } else if (process.env.NODE_ENV !== 'production') {
    warn(
      `v-else used on element <${el.tag}> without corresponding v-if.`
    )
  }
}

function processOnce (el) {
  const once = getAndRemoveAttr(el, 'v-once')
  if (once != null) {
    el.once = true
  }
}

function processSlot (el) {
  if (el.tag === 'slot') {
    el.slotName = getBindingAttr(el, 'name')
  } else {
    const slotTarget = getBindingAttr(el, 'slot')
    if (slotTarget) {
      el.slotTarget = slotTarget
    }
  }
}

function processComponent (el) {
  let binding
  if ((binding = getBindingAttr(el, 'is'))) {
    el.component = binding
  }
  if (getAndRemoveAttr(el, 'inline-template') != null) {
    el.inlineTemplate = true
  }
}

function processAttrs (el) {
  const list = el.attrsList
  let i, l, name, rawName, value, arg, modifiers, isProp
  for (i = 0, l = list.length; i < l; i++) {
    name = rawName = list[i].name
    value = list[i].value
    if (dirRE.test(name)) {
      // mark element as dynamic
      el.hasBindings = true
      // modifiers
      modifiers = parseModifiers(name)
      if (modifiers) {
        name = name.replace(modifierRE, '')
      }
      if (bindRE.test(name)) { // v-bind
        name = name.replace(bindRE, '')
        if (modifiers && modifiers.prop) {
          isProp = true
          name = camelize(name)
          if (name === 'innerHtml') name = 'innerHTML'
        }
        if (isProp || platformMustUseProp(name)) {
          addProp(el, name, value)
        } else {
          addAttr(el, name, value)
        }
      } else if (onRE.test(name)) { // v-on
        name = name.replace(onRE, '')
        addHandler(el, name, value, modifiers)
      } else { // normal directives
        name = name.replace(dirRE, '')
        // parse arg
        const argMatch = name.match(argRE)
        if (argMatch && (arg = argMatch[1])) {
          name = name.slice(0, -(arg.length + 1))
        }
        addDirective(el, name, rawName, value, arg, modifiers)
        if (process.env.NODE_ENV !== 'production' && name === 'model') {
          checkForAliasModel(el, value)
        }
      }
    } else {
      // literal attribute
      if (process.env.NODE_ENV !== 'production') {
        const expression = parseText(value, delimiters)
        if (expression) {
          warn(
            `${name}="${value}": ` +
            'Interpolation inside attributes has been deprecated. ' +
            'Use v-bind or the colon shorthand instead.'
          )
        }
      }
      addAttr(el, name, JSON.stringify(value))
    }
  }
}

function checkInFor (el: ASTElement): boolean {
  let parent = el
  while (parent) {
    if (parent.for !== undefined) {
      return true
    }
    parent = parent.parent
  }
  return false
}

function parseModifiers (name: string): Object | void {
  const match = name.match(modifierRE)
  if (match) {
    const ret = {}
    match.forEach(m => { ret[m.slice(1)] = true })
    return ret
  }
}

function makeAttrsMap (attrs: Array<Object>): Object {
  const map = {}
  for (let i = 0, l = attrs.length; i < l; i++) {
    if (process.env.NODE_ENV !== 'production' && map[attrs[i].name]) {
      warn('duplicate attribute: ' + attrs[i].name)
    }
    map[attrs[i].name] = attrs[i].value
  }
  return map
}

function findPrevElement (children: Array<any>): ASTElement | void {
  let i = children.length
  while (i--) {
    if (children[i].tag) return children[i]
  }
}

function isForbiddenTag (el): boolean {
  return (
    el.tag === 'style' ||
    (el.tag === 'script' && (
      !el.attrsMap.type ||
      el.attrsMap.type === 'text/javascript'
    ))
  )
}

const ieNSBug = /^xmlns:NS\d+/
const ieNSPrefix = /^NS\d+:/

/* istanbul ignore next */
function guardIESVGBug (attrs) {
  const res = []
  for (let i = 0; i < attrs.length; i++) {
    const attr = attrs[i]
    if (!ieNSBug.test(attr.name)) {
      attr.name = attr.name.replace(ieNSPrefix, '')
      res.push(attr)
    }
  }
  return res
}

function checkForAliasModel (el, value) {
  let _el = el
  while (_el) {
    if (_el.for && _el.alias === value) {
      warn(
        `<${el.tag} v-model="${value}">: ` +
        `You are binding v-model directly to a v-for iteration alias. ` +
        `This will not be able to modify the v-for source array because ` +
        `writing to the alias is like modifying a function local variable. ` +
        `Consider using an array of objects and use v-model on an object property instead.`
      )
    }
    _el = _el.parent
  }
}
<commit_msg>disable duplicate attribute warning in IE (close #3929)<commit_after>/* @flow */

import { decodeHTML } from 'entities'
import { parseHTML } from './html-parser'
import { parseText } from './text-parser'
import { cached, no, camelize } from 'shared/util'
import {
  pluckModuleFunction,
  getAndRemoveAttr,
  addProp,
  addAttr,
  addHandler,
  addDirective,
  getBindingAttr,
  baseWarn
} from '../helpers'

export const dirRE = /^v-|^@|^:/
export const forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/
export const forIteratorRE = /\(([^,]*),([^,]*)(?:,([^,]*))?\)/
const bindRE = /^:|^v-bind:/
const onRE = /^@|^v-on:/
const argRE = /:(.*)$/
const modifierRE = /\.[^\.]+/g
const specialNewlineRE = /\u2028|\u2029/g

const decodeHTMLCached = cached(decodeHTML)

// configurable state
let warn
let platformGetTagNamespace
let platformMustUseProp
let platformIsPreTag
let preTransforms
let transforms
let postTransforms
let delimiters

/**
 * Convert HTML string to AST.
 */
export function parse (
  template: string,
  options: CompilerOptions
): ASTElement | void {
  warn = options.warn || baseWarn
  platformGetTagNamespace = options.getTagNamespace || no
  platformMustUseProp = options.mustUseProp || no
  platformIsPreTag = options.isPreTag || no
  preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
  transforms = pluckModuleFunction(options.modules, 'transformNode')
  postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
  delimiters = options.delimiters
  const stack = []
  const preserveWhitespace = options.preserveWhitespace !== false
  let root
  let currentParent
  let inVPre = false
  let inPre = false
  let warned = false
  parseHTML(template, {
    expectHTML: options.expectHTML,
    isUnaryTag: options.isUnaryTag,
    isFromDOM: options.isFromDOM,
    shouldDecodeTags: options.shouldDecodeTags,
    shouldDecodeNewlines: options.shouldDecodeNewlines,
    start (tag, attrs, unary) {
      // check namespace.
      // inherit parent ns if there is one
      const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)

      // handle IE svg bug
      /* istanbul ignore if */
      if (options.isIE && ns === 'svg') {
        attrs = guardIESVGBug(attrs)
      }

      const element: ASTElement = {
        type: 1,
        tag,
        attrsList: attrs,
        attrsMap: makeAttrsMap(attrs, options.isIE),
        parent: currentParent,
        children: []
      }
      if (ns) {
        element.ns = ns
      }

      if (process.env.VUE_ENV !== 'server' && isForbiddenTag(element)) {
        element.forbidden = true
        process.env.NODE_ENV !== 'production' && warn(
          'Templates should only be responsible for mapping the state to the ' +
          'UI. Avoid placing tags with side-effects in your templates, such as ' +
          `<${tag}>.`
        )
      }

      // apply pre-transforms
      for (let i = 0; i < preTransforms.length; i++) {
        preTransforms[i](element, options)
      }

      if (!inVPre) {
        processPre(element)
        if (element.pre) {
          inVPre = true
        }
      }
      if (platformIsPreTag(element.tag)) {
        inPre = true
      }
      if (inVPre) {
        processRawAttrs(element)
      } else {
        processFor(element)
        processIf(element)
        processOnce(element)
        processKey(element)

        // determine whether this is a plain element after
        // removing structural attributes
        element.plain = !element.key && !attrs.length

        processRef(element)
        processSlot(element)
        processComponent(element)
        for (let i = 0; i < transforms.length; i++) {
          transforms[i](element, options)
        }
        processAttrs(element)
      }

      function checkRootConstraints (el) {
        if (process.env.NODE_ENV !== 'production') {
          if (el.tag === 'slot' || el.tag === 'template') {
            warn(
              `Cannot use <${el.tag}> as component root element because it may ` +
              'contain multiple nodes:\n' + template
            )
          }
          if (el.attrsMap.hasOwnProperty('v-for')) {
            warn(
              'Cannot use v-for on stateful component root element because ' +
              'it renders multiple elements:\n' + template
            )
          }
        }
      }

      // tree management
      if (!root) {
        root = element
        checkRootConstraints(root)
      } else if (process.env.NODE_ENV !== 'production' && !stack.length && !warned) {
        // allow 2 root elements with v-if and v-else
        if (root.if && element.else) {
          checkRootConstraints(element)
          root.elseBlock = element
        } else {
          warned = true
          warn(
            `Component template should contain exactly one root element:\n\n${template}`
          )
        }
      }
      if (currentParent && !element.forbidden) {
        if (element.else) {
          processElse(element, currentParent)
        } else {
          currentParent.children.push(element)
          element.parent = currentParent
        }
      }
      if (!unary) {
        currentParent = element
        stack.push(element)
      }
      // apply post-transforms
      for (let i = 0; i < postTransforms.length; i++) {
        postTransforms[i](element, options)
      }
    },

    end () {
      // remove trailing whitespace
      const element = stack[stack.length - 1]
      const lastNode = element.children[element.children.length - 1]
      if (lastNode && lastNode.type === 3 && lastNode.text === ' ') {
        element.children.pop()
      }
      // pop stack
      stack.length -= 1
      currentParent = stack[stack.length - 1]
      // check pre state
      if (element.pre) {
        inVPre = false
      }
      if (platformIsPreTag(element.tag)) {
        inPre = false
      }
    },

    chars (text: string) {
      if (!currentParent) {
        if (process.env.NODE_ENV !== 'production' && !warned && text === template) {
          warned = true
          warn(
            'Component template requires a root element, rather than just text:\n\n' + template
          )
        }
        return
      }
      text = inPre || text.trim()
        ? decodeHTMLCached(text)
        // only preserve whitespace if its not right after a starting tag
        : preserveWhitespace && currentParent.children.length ? ' ' : ''
      if (text) {
        let expression
        if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) {
          currentParent.children.push({
            type: 2,
            expression,
            text
          })
        } else {
          // #3895 special character
          text = text.replace(specialNewlineRE, '')
          currentParent.children.push({
            type: 3,
            text
          })
        }
      }
    }
  })
  return root
}

function processPre (el) {
  if (getAndRemoveAttr(el, 'v-pre') != null) {
    el.pre = true
  }
}

function processRawAttrs (el) {
  const l = el.attrsList.length
  if (l) {
    const attrs = el.attrs = new Array(l)
    for (let i = 0; i < l; i++) {
      attrs[i] = {
        name: el.attrsList[i].name,
        value: JSON.stringify(el.attrsList[i].value)
      }
    }
  } else if (!el.pre) {
    // non root node in pre blocks with no attributes
    el.plain = true
  }
}

function processKey (el) {
  const exp = getBindingAttr(el, 'key')
  if (exp) {
    if (process.env.NODE_ENV !== 'production' && el.tag === 'template') {
      warn(`<template> cannot be keyed. Place the key on real elements instead.`)
    }
    el.key = exp
  }
}

function processRef (el) {
  const ref = getBindingAttr(el, 'ref')
  if (ref) {
    el.ref = ref
    el.refInFor = checkInFor(el)
  }
}

function processFor (el) {
  let exp
  if ((exp = getAndRemoveAttr(el, 'v-for'))) {
    const inMatch = exp.match(forAliasRE)
    if (!inMatch) {
      process.env.NODE_ENV !== 'production' && warn(
        `Invalid v-for expression: ${exp}`
      )
      return
    }
    el.for = inMatch[2].trim()
    const alias = inMatch[1].trim()
    const iteratorMatch = alias.match(forIteratorRE)
    if (iteratorMatch) {
      el.alias = iteratorMatch[1].trim()
      el.iterator1 = iteratorMatch[2].trim()
      if (iteratorMatch[3]) {
        el.iterator2 = iteratorMatch[3].trim()
      }
    } else {
      el.alias = alias
    }
  }
}

function processIf (el) {
  const exp = getAndRemoveAttr(el, 'v-if')
  if (exp) {
    el.if = exp
  }
  if (getAndRemoveAttr(el, 'v-else') != null) {
    el.else = true
  }
}

function processElse (el, parent) {
  const prev = findPrevElement(parent.children)
  if (prev && prev.if) {
    prev.elseBlock = el
  } else if (process.env.NODE_ENV !== 'production') {
    warn(
      `v-else used on element <${el.tag}> without corresponding v-if.`
    )
  }
}

function processOnce (el) {
  const once = getAndRemoveAttr(el, 'v-once')
  if (once != null) {
    el.once = true
  }
}

function processSlot (el) {
  if (el.tag === 'slot') {
    el.slotName = getBindingAttr(el, 'name')
  } else {
    const slotTarget = getBindingAttr(el, 'slot')
    if (slotTarget) {
      el.slotTarget = slotTarget
    }
  }
}

function processComponent (el) {
  let binding
  if ((binding = getBindingAttr(el, 'is'))) {
    el.component = binding
  }
  if (getAndRemoveAttr(el, 'inline-template') != null) {
    el.inlineTemplate = true
  }
}

function processAttrs (el) {
  const list = el.attrsList
  let i, l, name, rawName, value, arg, modifiers, isProp
  for (i = 0, l = list.length; i < l; i++) {
    name = rawName = list[i].name
    value = list[i].value
    if (dirRE.test(name)) {
      // mark element as dynamic
      el.hasBindings = true
      // modifiers
      modifiers = parseModifiers(name)
      if (modifiers) {
        name = name.replace(modifierRE, '')
      }
      if (bindRE.test(name)) { // v-bind
        name = name.replace(bindRE, '')
        if (modifiers && modifiers.prop) {
          isProp = true
          name = camelize(name)
          if (name === 'innerHtml') name = 'innerHTML'
        }
        if (isProp || platformMustUseProp(name)) {
          addProp(el, name, value)
        } else {
          addAttr(el, name, value)
        }
      } else if (onRE.test(name)) { // v-on
        name = name.replace(onRE, '')
        addHandler(el, name, value, modifiers)
      } else { // normal directives
        name = name.replace(dirRE, '')
        // parse arg
        const argMatch = name.match(argRE)
        if (argMatch && (arg = argMatch[1])) {
          name = name.slice(0, -(arg.length + 1))
        }
        addDirective(el, name, rawName, value, arg, modifiers)
        if (process.env.NODE_ENV !== 'production' && name === 'model') {
          checkForAliasModel(el, value)
        }
      }
    } else {
      // literal attribute
      if (process.env.NODE_ENV !== 'production') {
        const expression = parseText(value, delimiters)
        if (expression) {
          warn(
            `${name}="${value}": ` +
            'Interpolation inside attributes has been deprecated. ' +
            'Use v-bind or the colon shorthand instead.'
          )
        }
      }
      addAttr(el, name, JSON.stringify(value))
    }
  }
}

function checkInFor (el: ASTElement): boolean {
  let parent = el
  while (parent) {
    if (parent.for !== undefined) {
      return true
    }
    parent = parent.parent
  }
  return false
}

function parseModifiers (name: string): Object | void {
  const match = name.match(modifierRE)
  if (match) {
    const ret = {}
    match.forEach(m => { ret[m.slice(1)] = true })
    return ret
  }
}

function makeAttrsMap (attrs: Array<Object>, isIE: ?boolean): Object {
  const map = {}
  for (let i = 0, l = attrs.length; i < l; i++) {
    if (process.env.NODE_ENV !== 'production' && map[attrs[i].name] && !isIE) {
      warn('duplicate attribute: ' + attrs[i].name)
    }
    map[attrs[i].name] = attrs[i].value
  }
  return map
}

function findPrevElement (children: Array<any>): ASTElement | void {
  let i = children.length
  while (i--) {
    if (children[i].tag) return children[i]
  }
}

function isForbiddenTag (el): boolean {
  return (
    el.tag === 'style' ||
    (el.tag === 'script' && (
      !el.attrsMap.type ||
      el.attrsMap.type === 'text/javascript'
    ))
  )
}

const ieNSBug = /^xmlns:NS\d+/
const ieNSPrefix = /^NS\d+:/

/* istanbul ignore next */
function guardIESVGBug (attrs) {
  const res = []
  for (let i = 0; i < attrs.length; i++) {
    const attr = attrs[i]
    if (!ieNSBug.test(attr.name)) {
      attr.name = attr.name.replace(ieNSPrefix, '')
      res.push(attr)
    }
  }
  return res
}

function checkForAliasModel (el, value) {
  let _el = el
  while (_el) {
    if (_el.for && _el.alias === value) {
      warn(
        `<${el.tag} v-model="${value}">: ` +
        `You are binding v-model directly to a v-for iteration alias. ` +
        `This will not be able to modify the v-for source array because ` +
        `writing to the alias is like modifying a function local variable. ` +
        `Consider using an array of objects and use v-model on an object property instead.`
      )
    }
    _el = _el.parent
  }
}
<|endoftext|>


<commit_before>var format = require('pg-format');
var debug = require('debug')('communes');

function intersects(options) {
    options = options || {};
    var refs = {
        'osm': {
            prepareQuery: function (geometry) {
                return format(`
                    SELECT nom, insee, ST_Area(ST_Intersection(input.geom, communes.geom)::geography) / 10000 AS intersect_area
                    FROM
                        communes,
                        (SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) geom) input
                    WHERE ST_Intersects(communes.geom, input.geom);
                `, geometry);
            }
        },
        'ign-parcellaire': {
            prepareQuery: function (geometry) {
                return format(`
                    SELECT NOM_COM as nom, CODE_INSEE as insee, ST_Area(ST_Intersection(input.geom, communes_ign.geom)::geography) / 10000 AS intersect_area
                    FROM
                        communes_ign,
                        (SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) geom) input
                    WHERE ST_Intersects(communes_ign.geom, input.geom);
                `, geometry);
            }
        }
    };

    return function (req, res, next) {
        if (!req.body || !req.body.geom) {
            res.status(400).send({ code: 400, message: 'Requête incorrecte' });
            return;
        }

        var ref = refs[options.ref || 'osm'];
        var sqlQuery = ref.prepareQuery(req.body.geom.geometry);

        req.pgClient.query(sqlQuery, (err, result) => {
            if (err) {
                req.pgEnd(err);
                return next(err);
            }

            if (result.rows.length === 0) {
                return res.status(400).send({ code: 400, message: 'Aucune commune correspondant à cette localisation. Hors de France ?' });
            }

            req.intersectedCommunes = result.rows;
            debug('%d communes trouvée(s)', req.intersectedCommunes.length);
            req.intersectedCommunes.forEach((commune) => debug('- %s', commune.nom));
            next();
        });
    };
}

exports.intersects = intersects;
<commit_msg>Compute geography and contains property in communes helper<commit_after>var format = require('pg-format');
var debug = require('debug')('communes');

function intersects(options) {
    options = options || {};
    var refs = {
        'osm': {
            prepareQuery: function (geometry) {
                return format(`
                    SELECT
                        nom,
                        insee,
                        ST_Contains(input.geom, communes.geom) AS contains,
                        ST_Area(ST_Intersection(input.geom, communes.geom)::geography) / 10000 AS intersect_area,
                        ST_AsGeoJSON(communes.geom) AS geom,
                    FROM
                        communes,
                        (SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) geom) input
                    WHERE ST_Intersects(communes.geom, input.geom);
                `, geometry);
            }
        },
        'ign-parcellaire': {
            prepareQuery: function (geometry) {
                return format(`
                    SELECT
                        NOM_COM as nom,
                        CODE_INSEE as insee,
                        ST_Contains(input.geom, communes_ign.geom) AS contains,
                        ST_Area(ST_Intersection(input.geom, communes_ign.geom)::geography) / 10000 AS intersect_area,
                        ST_AsGeoJSON(communes_ign.geom) AS geom
                    FROM
                        communes_ign,
                        (SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) geom) input
                    WHERE ST_Intersects(communes_ign.geom, input.geom);
                `, geometry);
            }
        }
    };

    return function (req, res, next) {
        if (!req.body || !req.body.geom) {
            res.status(400).send({ code: 400, message: 'Requête incorrecte' });
            return;
        }

        var ref = refs[options.ref || 'osm'];
        var sqlQuery = ref.prepareQuery(req.body.geom.geometry);

        req.pgClient.query(sqlQuery, (err, result) => {
            if (err) {
                req.pgEnd(err);
                return next(err);
            }

            if (result.rows.length === 0) {
                return res.status(400).send({ code: 400, message: 'Aucune commune correspondant à cette localisation. Hors de France ?' });
            }

            req.intersectedCommunes = result.rows;
            debug('%d communes trouvée(s)', req.intersectedCommunes.length);
            req.intersectedCommunes.forEach((commune) => debug('- %s', commune.nom));
            next();
        });
    };
}

exports.intersects = intersects;
<|endoftext|>


<commit_before>
/*!
 * Jade - self closing tags
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

module.exports = [
    'area'
  , 'base'
  , 'br'
  , 'col'
  , 'embed'
  , 'hr'
  , 'img'
  , 'input'
  , 'keygen'
  , 'link'
  , 'menuitem'
  , 'meta'
  , 'param'
  , 'source'
  , 'track'
  , 'wbr'
];
<commit_msg>Add link to source of void elements<commit_after>
/*!
 * Jade - self closing tags
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

// source: http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements

module.exports = [
    'area'
  , 'base'
  , 'br'
  , 'col'
  , 'embed'
  , 'hr'
  , 'img'
  , 'input'
  , 'keygen'
  , 'link'
  , 'menuitem'
  , 'meta'
  , 'param'
  , 'source'
  , 'track'
  , 'wbr'
];
<|endoftext|>


<commit_before>/* Helper method for festival TTS engine */<commit_msg>Added basic wrapper for festival tts module<commit_after>/* Helper method for festival TTS engine */

var exec = require('child_process').exec;
var festivalCmd = "festival -TTS ";

var _speak = function(text){
	cmd = festivalCmd + text;
	exec(festivalCmd, function(error, stdout, stderr){

	})
}

/* Speaks the text out */
exports.speak = function(text){
	_exec(text);
}<|endoftext|>


<commit_before>import React from 'react';
import SkyLight from 'react-skylight';
import Glyph from '../../img/cart.png';
import Increment from './increment';
import Decrement from './decrement';
import Remove from './remove';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {clearCart} from '../../store/actions';

const currency = n => n.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');

export class Cart extends React.Component {
  constructor() {
    super();
    this.state = {};
  }

  handleClick() {
    this.refs.customDialog.show();
  }

  sendOrder() {
    //send order by e-mail
    console.info('Order should be sent by the API.');
    console.info('Order: ', this.props.cart.map((i) => {
      return {
          name: i.name || 'N/A',
          code: i.code || 'N/A',
          description: i.description || 'N/A',
          quantity: i.quantity
        }
    }));
    this.props.clearCart();
    this.refs.customDialog.hide();
  }

  render() {
    var total = 0;

    return (

        <SkyLight hideOnOverlayClicked ref="customDialog" title='Cart'>

              Name
              Description
              Price
              Quantity
              SubTotal
              Remove

            {
              this.props.cart.map((i) => {
                const {id, name, price, quantity, description} = i;
                total += (price * quantity)
                return (

                    {name || '-'}
                    {description || '-'}
                    {currency((price > 0) ? price : 0) + '$'}

                      <Increment name={name} quantity={quantity}/> {quantity} <Decrement name={name} quantity={quantity}/>

                    {currency(((price > 0) ? price : 0) * quantity) + '$'}
                    <Remove name={name}/>

                )
              })
            }

              Total = {currency(total) + '$'}
              <button onClick={this.sendOrder.bind(this)}>Send order</button>

        </SkyLight>

    );
  }
};

var mapStateToProps = (store) => {
  return{
    cart: store.cart
  };
};

var matchDispatchToProps = (dispatch) => {
  return bindActionCreators({clearCart: clearCart}, dispatch)
};

export default connect(mapStateToProps, matchDispatchToProps)(Cart);
<commit_msg>forgot to fix cart total<commit_after>import React from 'react';
import SkyLight from 'react-skylight';
import Glyph from '../../img/cart.png';
import Increment from './increment';
import Decrement from './decrement';
import Remove from './remove';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {clearCart} from '../../store/actions';

const currency = n => n.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');

export class Cart extends React.Component {
  constructor() {
    super();
    this.state = {};
  }

  handleClick() {
    this.refs.customDialog.show();
  }

  sendOrder() {
    //send order by e-mail
    console.info('Order should be sent by the API.');
    console.info('Order: ', this.props.cart.map((i) => {
      return {
          name: i.name || 'N/A',
          code: i.code || 'N/A',
          description: i.description || 'N/A',
          quantity: i.quantity
        }
    }));
    this.props.clearCart();
    this.refs.customDialog.hide();
  }

  render() {
    var total = 0;

    return (

        <SkyLight hideOnOverlayClicked ref="customDialog" title='Cart'>

              Name
              Description
              Price
              Quantity
              SubTotal
              Remove

            {
              this.props.cart.map((i) => {
                const {id, name, price, quantity, description} = i;
                total += (((price > 0) ? price : 0) * quantity)
                return (

                    {name || '-'}
                    {description || '-'}
                    {currency((price > 0) ? price : 0) + '$'}

                      <Increment name={name} quantity={quantity}/> {quantity} <Decrement name={name} quantity={quantity}/>

                    {currency(((price > 0) ? price : 0) * quantity) + '$'}
                    <Remove name={name}/>

                )
              })
            }

              Total = {currency(total) + '$'}
              <button onClick={this.sendOrder.bind(this)}>Send order</button>

        </SkyLight>

    );
  }
};

var mapStateToProps = (store) => {
  return{
    cart: store.cart
  };
};

var matchDispatchToProps = (dispatch) => {
  return bindActionCreators({clearCart: clearCart}, dispatch)
};

export default connect(mapStateToProps, matchDispatchToProps)(Cart);
<|endoftext|>


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

import React from 'react'
import {MOCK_OBSERVED_USERS_LIST} from '@canvas/observer-picker/react/__tests__/fixtures'
import {OBSERVER_COOKIE_PREFIX} from '@canvas/observer-picker/ObserverGetObservee'
import {act, render, waitFor} from '@testing-library/react'
import K5Dashboard from '../K5Dashboard'
import moxios from 'moxios'
import {opportunities, defaultK5DashboardProps as defaultProps, defaultEnv} from './mocks'
import {resetCardCache} from '@canvas/dashboard-card'
import {MOCK_CARDS, MOCK_CARDS_2} from '@canvas/k5/react/__tests__/fixtures'

const currentUserId = defaultProps.currentUser.id
const observedUserCookieName = `${OBSERVER_COOKIE_PREFIX}${currentUserId}`

describe('K5Dashboard Parent Support', () => {
  beforeEach(() => {
    document.cookie = `${observedUserCookieName}=4;path=/`
    moxios.install()
    global.ENV = defaultEnv
  })
  afterEach(() => {
    moxios.uninstall()
    global.ENV = {}
    resetCardCache()
  })
  const opportunities2 = [
    {
      id: '3',
      course_id: '23',
      name: 'A new Assignment',
      points_possible: 10,
      html_url: '/courses/23/assignments/3',
      due_at: '2021-02-15T05:59:00Z',
      submission_types: ['online_text_entry']
    }
  ]

  it('shows picker when user is an observer', () => {
    const {getByRole} = render(
      <K5Dashboard
        {...defaultProps}
        canAddObservee
        currentUserRoles={['user', 'observer']}
        observedUsersList={MOCK_OBSERVED_USERS_LIST}
      />
    )
    const select = getByRole('combobox', {name: 'Select a student to view'})
    expect(select).toBeInTheDocument()
    expect(select.value).toBe('Student 4')
  })

  it('prefetches dashboard cards with the correct url param', async done => {
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=4', {
      status: 200,
      response: MOCK_CARDS
    })

    render(
      <K5Dashboard
        {...defaultProps}
        currentUserRoles={['user', 'observer', 'teacher']}
        canAddObservee
        observedUsersList={MOCK_OBSERVED_USERS_LIST}
      />
    )
    // let the dashboard execute all its queries and render
    await waitFor(
      () => {
        expect(moxios.requests.mostRecent()).not.toBeNull()
      },
      {timeout: 5000}
    )
    const preFetchedRequest = moxios.requests.mostRecent()
    expect(preFetchedRequest.url).toBe('/api/v1/dashboard/dashboard_cards?observed_user_id=4')
    expect(moxios.requests.count()).toBe(1)
    done()
  })

  it('does not make a request if the user has been already requested', async () => {
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=4', {
      status: 200,
      response: MOCK_CARDS
    })
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=2', {
      status: 200,
      response: MOCK_CARDS_2
    })
    const {findByText, getByRole, getByText, queryByText} = render(
      <K5Dashboard
        {...defaultProps}
        currentUserRoles={['user', 'observer', 'teacher']}
        canAddObservee
        observedUsersList={MOCK_OBSERVED_USERS_LIST}
      />
    )
    expect(await findByText('Economics 101')).toBeInTheDocument()
    expect(queryByText('Economics 203')).not.toBeInTheDocument()
    const select = getByRole('combobox', {name: 'Select a student to view'})
    expect(select.value).toBe('Student 4')
    expect(moxios.requests.mostRecent().url).toBe(
      '/api/v1/dashboard/dashboard_cards?observed_user_id=4'
    )
    act(() => select.click())
    act(() => getByText('Student 2').click())
    expect(await findByText('Economics 203')).toBeInTheDocument()
    expect(queryByText('Economics 101')).not.toBeInTheDocument()
    expect(moxios.requests.mostRecent().url).toBe(
      '/api/v1/dashboard/dashboard_cards?observed_user_id=2'
    )
    act(() => select.click())
    act(() => getByText('Student 4').click())
    expect(await findByText('Economics 101')).toBeInTheDocument()
    expect(queryByText('Economics 203')).not.toBeInTheDocument()
    // Should not fetch student 4's cards again; they've been cached
    expect(moxios.requests.mostRecent().url).toBe(
      '/api/v1/dashboard/dashboard_cards?observed_user_id=2'
    )
    // 2 total requests - one for student 4, one for student 2
    expect(moxios.requests.count()).toBe(2)
  })

  it('shows the observee missing items on dashboard cards', async () => {
    moxios.stubs.reset()
    moxios.requests.reset()
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=4', {
      status: 200,
      response: MOCK_CARDS
    })
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=2', {
      status: 200,
      response: MOCK_CARDS_2
    })
    moxios.stubRequest(/\/api\/v1\/users\/self\/missing_submissions\?.*observed_user_id=4.*/, {
      status: 200,
      headers: {link: 'url; rel="current"'},
      response: opportunities
    })
    moxios.stubRequest(/\/api\/v1\/users\/self\/missing_submissions\?.*observed_user_id=2.*/, {
      status: 200,
      headers: {link: 'url; rel="current"'},
      response: opportunities2
    })
    // createPlannerMocks()

    const {getByText, findByRole, getByRole} = render(
      <K5Dashboard
        {...defaultProps}
        currentUserRoles={['user', 'observer', 'teacher']}
        observedUsersList={MOCK_OBSERVED_USERS_LIST}
        canAddObservee
        plannerEnabled
      />
    )
    // let the dashboard execute all its queries and render
    await waitFor(
      () => {
        expect(document.querySelectorAll('.ic-DashboardCard').length).toBeGreaterThan(0)
      },
      {timeout: 5000}
    )
    expect(
      await findByRole('link', {
        name: 'View 2 missing items for course Economics 101',
        timeout: 5000,
        exact: false
      })
    ).toBeInTheDocument()
    const observerSelect = getByRole('combobox', {name: 'Select a student to view'})
    act(() => observerSelect.click())
    act(() => getByText('Student 2').click())
    expect(
      await findByRole('link', {
        name: 'View 1 missing items for course Economics 203',
        timeout: 5000,
        exact: false
      })
    ).toBeInTheDocument()
  })
})
<commit_msg>Skip flaky unit test on k5dashboardObserver.test.js<commit_after>/*
 * Copyright (C) 2022 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import React from 'react'
import {MOCK_OBSERVED_USERS_LIST} from '@canvas/observer-picker/react/__tests__/fixtures'
import {OBSERVER_COOKIE_PREFIX} from '@canvas/observer-picker/ObserverGetObservee'
import {act, render, waitFor} from '@testing-library/react'
import K5Dashboard from '../K5Dashboard'
import moxios from 'moxios'
import {opportunities, defaultK5DashboardProps as defaultProps, defaultEnv} from './mocks'
import {resetCardCache} from '@canvas/dashboard-card'
import {MOCK_CARDS, MOCK_CARDS_2} from '@canvas/k5/react/__tests__/fixtures'

const currentUserId = defaultProps.currentUser.id
const observedUserCookieName = `${OBSERVER_COOKIE_PREFIX}${currentUserId}`

describe('K5Dashboard Parent Support', () => {
  beforeEach(() => {
    document.cookie = `${observedUserCookieName}=4;path=/`
    moxios.install()
    global.ENV = defaultEnv
  })
  afterEach(() => {
    moxios.uninstall()
    global.ENV = {}
    resetCardCache()
  })
  const opportunities2 = [
    {
      id: '3',
      course_id: '23',
      name: 'A new Assignment',
      points_possible: 10,
      html_url: '/courses/23/assignments/3',
      due_at: '2021-02-15T05:59:00Z',
      submission_types: ['online_text_entry']
    }
  ]

  it('shows picker when user is an observer', () => {
    const {getByRole} = render(
      <K5Dashboard
        {...defaultProps}
        canAddObservee
        currentUserRoles={['user', 'observer']}
        observedUsersList={MOCK_OBSERVED_USERS_LIST}
      />
    )
    const select = getByRole('combobox', {name: 'Select a student to view'})
    expect(select).toBeInTheDocument()
    expect(select.value).toBe('Student 4')
  })

  it('prefetches dashboard cards with the correct url param', async done => {
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=4', {
      status: 200,
      response: MOCK_CARDS
    })

    render(
      <K5Dashboard
        {...defaultProps}
        currentUserRoles={['user', 'observer', 'teacher']}
        canAddObservee
        observedUsersList={MOCK_OBSERVED_USERS_LIST}
      />
    )
    // let the dashboard execute all its queries and render
    await waitFor(
      () => {
        expect(moxios.requests.mostRecent()).not.toBeNull()
      },
      {timeout: 5000}
    )
    const preFetchedRequest = moxios.requests.mostRecent()
    expect(preFetchedRequest.url).toBe('/api/v1/dashboard/dashboard_cards?observed_user_id=4')
    expect(moxios.requests.count()).toBe(1)
    done()
  })

  it('does not make a request if the user has been already requested', async () => {
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=4', {
      status: 200,
      response: MOCK_CARDS
    })
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=2', {
      status: 200,
      response: MOCK_CARDS_2
    })
    const {findByText, getByRole, getByText, queryByText} = render(
      <K5Dashboard
        {...defaultProps}
        currentUserRoles={['user', 'observer', 'teacher']}
        canAddObservee
        observedUsersList={MOCK_OBSERVED_USERS_LIST}
      />
    )
    expect(await findByText('Economics 101')).toBeInTheDocument()
    expect(queryByText('Economics 203')).not.toBeInTheDocument()
    const select = getByRole('combobox', {name: 'Select a student to view'})
    expect(select.value).toBe('Student 4')
    expect(moxios.requests.mostRecent().url).toBe(
      '/api/v1/dashboard/dashboard_cards?observed_user_id=4'
    )
    act(() => select.click())
    act(() => getByText('Student 2').click())
    expect(await findByText('Economics 203')).toBeInTheDocument()
    expect(queryByText('Economics 101')).not.toBeInTheDocument()
    expect(moxios.requests.mostRecent().url).toBe(
      '/api/v1/dashboard/dashboard_cards?observed_user_id=2'
    )
    act(() => select.click())
    act(() => getByText('Student 4').click())
    expect(await findByText('Economics 101')).toBeInTheDocument()
    expect(queryByText('Economics 203')).not.toBeInTheDocument()
    // Should not fetch student 4's cards again; they've been cached
    expect(moxios.requests.mostRecent().url).toBe(
      '/api/v1/dashboard/dashboard_cards?observed_user_id=2'
    )
    // 2 total requests - one for student 4, one for student 2
    expect(moxios.requests.count()).toBe(2)
  })
  // Skipping for flakiness. See https://instructure.atlassian.net/browse/LS-3048.
  it.skip('shows the observee missing items on dashboard cards', async () => {
    moxios.stubs.reset()
    moxios.requests.reset()
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=4', {
      status: 200,
      response: MOCK_CARDS
    })
    moxios.stubRequest('/api/v1/dashboard/dashboard_cards?observed_user_id=2', {
      status: 200,
      response: MOCK_CARDS_2
    })
    moxios.stubRequest(/\/api\/v1\/users\/self\/missing_submissions\?.*observed_user_id=4.*/, {
      status: 200,
      headers: {link: 'url; rel="current"'},
      response: opportunities
    })
    moxios.stubRequest(/\/api\/v1\/users\/self\/missing_submissions\?.*observed_user_id=2.*/, {
      status: 200,
      headers: {link: 'url; rel="current"'},
      response: opportunities2
    })
    // createPlannerMocks()

    const {getByText, findByRole, getByRole} = render(
      <K5Dashboard
        {...defaultProps}
        currentUserRoles={['user', 'observer', 'teacher']}
        observedUsersList={MOCK_OBSERVED_USERS_LIST}
        canAddObservee
        plannerEnabled
      />
    )
    // let the dashboard execute all its queries and render
    await waitFor(
      () => {
        expect(document.querySelectorAll('.ic-DashboardCard').length).toBeGreaterThan(0)
      },
      {timeout: 5000}
    )
    expect(
      await findByRole('link', {
        name: 'View 2 missing items for course Economics 101',
        timeout: 5000,
        exact: false
      })
    ).toBeInTheDocument()
    const observerSelect = getByRole('combobox', {name: 'Select a student to view'})
    act(() => observerSelect.click())
    act(() => getByText('Student 2').click())
    expect(
      await findByRole('link', {
        name: 'View 1 missing items for course Economics 203',
        timeout: 5000,
        exact: false
      })
    ).toBeInTheDocument()
  })
})
<|endoftext|>


<commit_before>(function() {
  var Semanticolor, SemanticolorView, CompositeDisposable;

  SemanticolorView = require('./semanticolor-view');

  CompositeDisposable = require('atom').CompositeDisposable;

  var $ = require("atom-space-pen-views").$;
  var _ = require('lodash');

  var fs = require('fs');
  var path = require('path');

  var thisDir = path.dirname(__dirname);
  var lessFile = thisDir + '/styles/semanticolor.less';

  function hash(string) {
    return string.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
    var chr, hash, i, len;
    hash = 0;
    if (string.length === 0) {
      return hash;
    }
    i = 0;
    len = string.length;
    while (i < len) {
      chr = string.charCodeAt(i);
      hash = ((hash << 5) - hash) + chr;
      hash |= 0;
      i++;
    }
    return hash;
  }

  function colorIndex(string) {
    return (Math.abs(hash(string)) % colorDiversity) + 1;
  }

  var enabledGrammars = ".php";
  var enabledSourceObjects = ".variable, .function, .function-call";
  var colorDiversity = 96;
  var excludeSourceSelector = ".comment";
  var updatedCd = null;

  var affectedSourceSelector = ".variable, .function, .function-call, .entity, .class";
  module.exports = Semanticolor = {
    config: {
  		/*affectedGrammars: {
  			type: 'object',
  			properties: {
          enablePHP: {
            title: 'PHP Enabled',
            description: "Enable the PHP source selector \'.php\'",
            type: 'boolean',
            value: '.php',
            default: true
          },
          enableJavaScript: {
            title: 'JavaScript Enabled',
            description: "Enable the PHP source selector \'.js\' (Since Atom does not parse JS proper you need the language-javascript-semantic package)",
            type: 'boolean',
            value: '.js',
            default: true
          },
          enablePython: {
            title: 'Python Enabled',
            description: "Enable the PHP source selector \'.python\'",
            type: 'boolean',
            value: '.python',
            default: true
          },
          enableCoffeeScript: {
            title: 'CoffeeScript Enabled',
            description: "Enable the CoffeeScript source selector \'.coffee\'",
            type: 'boolean',
            value: '.coffee',
            default: true
          },
          enableHTML: {
            title: 'HTML Enabled',
            description: "Enable the HTML source selector \'.html\'",
            type: 'boolean',
            value: '.html',
            default: true
          },
          additionalGrammarSelectors: {
            title: 'Define additional Grammars by their selector',
            description: "Usually along the lines of \'.somelanguage\'",
            type: 'string',
            default: ''
          }
        }
  		},*/
      affectedSourceObjects: {
        title: 'Source objects to colorize',
        description: "What grammars you want to colorize. All objects of the same name are colored the same throughout",
  			type: 'object',
  			properties: {
          enableVariables: {
            title: 'Variables Enabled',
            description: "Enable variable selector \'.variable\'",
            type: 'boolean',
            default: true,
            value: '.variable'
          },
          enableFunctions: {
            title: 'Functions Enabled',
            description: "Enable function selector \'.function\'",
            type: 'boolean',
            default: true,
            value: '.function'
          },
          enableFunctionCalls: {
            title: 'Function Calls Enabled',
            description: "Enable function calls selector \'.function-call\'",
            type: 'boolean',
            default: true,
            value: '.function-call'
          },
          enableEntities: {
            title: 'Class Entities',
            description: "Enable entity selector \'.entity\'",
            type: 'boolean',
            default: true,
            value: '.entity'
          },
          enableClasses: {
            title: 'Class Names',
            description: "Enable class selector \'.class\'",
            type: 'boolean',
            default: true,
            value: '.class'
          },
          enableConstants: {
            title: 'Constants',
            description: "Enable constants selector \'.constant\'",
            type: 'boolean',
            default: true,
            value: '.constant'
          },
          enableHtmlTags: {
            title: 'HTML Tags',
            description: "Enable function selector \'.block\'",
            type: 'boolean',
            default: true,
            value: '.block'
          },
          additionalSelectors: {
            title: 'Define additional selectors for colorizing. All similar innerTexts are the same color.',
            description: "Usually along the lines of \'.comment, .example\'. Must be comma delimited!",
            type: 'string',
            default: ''
          }
        }
  		},
  		/*affectedSourceSelector: {
  			type: 'string',
  			'default': ".variable, .function, .function-call, .entity, .class, .block.html"
  		},*/
  		excludeSourceSelector: {
  			type: 'string',
  			'default': '.comment'
  		},
      colorDiversity: {
        type: 'integer',
        text: 'Affects how diverse the colors are.',
        description: 'Higher number will allow for more unique tokens and colors. Lower will limit the colors used.',
        minimum: 8,
        default: 96,
        maximum: 360
      },
      saturationLevelA1: {
        type: 'integer',
        text: 'Used for generating the additional colors.',
        description: 'Higher number will allow for more unique tokens and colors. Lower will limit the colors used.',
        minimum: 0,
        default: 100,
        maximum: 100
      },
      saturationLevelA2: {
        type: 'integer',
        text: 'Used for generating the additional colors.',
        description: 'Higher number will allow for more unique tokens and colors. Lower will limit the colors used.',
        minimum: 0,
        default: 10,
        maximum: 100
      },
      saturationLevelB1: {
        type: 'integer',
        text: 'Used for generating the additional colors.',
        description: 'Higher number will allow for more unique tokens and colors. Lower will limit the colors used.',
        minimum: 0,
        default: 75,
        maximum: 100
      },
      saturationLevelB2: {
        type: 'integer',
        text: 'Used for generating the additional colors.',
        description: 'Higher number will allow for more unique tokens and colors. Lower will limit the colors used.',
        minimum: 0,
        default: 75,
        maximum: 100
      }
  	},
    semanticolorView: SemanticolorView,
    modalPanel: SemanticolorView,
    selector: '.source',
    disableForSelector: '',
    subscriptions: null,
    updateColors: function(loc){
      loc.find(enabledSourceObjects).each(function(i, el) {
        if ($(el).is(excludeSourceSelector)) return;
        /*if (!$(el).is(enabledGrammars)) return;*/
        var text = el.innerText;
        var semanticClass = "identifier semanticolor-" + colorIndex(text);
        $(el).addClass(semanticClass);
      });
    },
    updateLESSStylesheet: function(){
      var cd = atom.config.get('semanticolor').colorDiversity;
      var sla1 = atom.config.get('semanticolor').saturationLevelA1 || 100;
      var sla2 = atom.config.get('semanticolor').saturationLevelA2 || 10;
      var slb1 = atom.config.get('semanticolor').saturationLevelB1 || 75;
      var slb2 = atom.config.get('semanticolor').saturationLevelB2 || 75;
      /*var lessUpdate = "@colorDiversity: " + cd + ';';*/
      var lessUpdate = [
        "@colorDiversity: "+cd+";",
        "@saturationLevelA1: "+sla1+"%;",
        "@saturationLevelA2: "+sla2+"%;",
        "@saturationLevelB1: "+slb1+"%;",
        "@saturationLevelB2: "+slb2+"%;"
      ].join("\n");

      /*var lessUpdate = lessUpdateArray.join("\n");*/
      console.log(lessUpdate);
      var written = false;
      try {
        var currentLess = fs.readFileSync(lessFile);
        if (currentLess != lessUpdate){
          fs.writeFileSync(lessFile, lessUpdate);
        }
        atom.notifications.addSuccess('Rewrote new colorDiversity...',{detail: 'Reloading with stylesheet of {'+colorDiversity+'} possible colors.'});
        this.confirmReload();
      } catch(e) {
        if (e.code === 'ENOENT') {
            atom.notifications.addError('No initial colorDiversity configured...',{detail: 'Updating stylesheet with default of {'+colorDiversity+'} possible colors.'});
            try {
              // var lessUpdate = "@colorDiversity: " + 96 + ';';
              var lessUpdate = [
                "@colorDiversity: 96;",
                "@saturationLevelA1: 100%;",
                "@saturationLevelA2: 10%;",
                "@saturationLevelB1: 75%;",
                "@saturationLevelB2: 75%;"
              ].join("\n");
              written = fs.writeFileSync(lessFile, lessUpdate);
            } catch(err) {
              console.log(err);
              atom.notifications.addError(e.code + ' : ' + e.message, {detail: 'Something failed not sure what. Open an issue with me!'});
            }
          } else {
            atom.notifications.addError('No initial colorDiversity configured...',{detail: 'Updating stylesheet with default of {'+colorDiversity+'} possible colors.'});
          }
      } finally {

      }
    },
    updateConfigRuntime: function(updateStylesheet){
      var config = atom.config.get('semanticolor');

      /*var enableGrammarsArray = [];
      _.each(Semanticolor.config.affectedGrammars.properties, function(v, i){
        var status = v.default;

        if (config.affectedGrammars[i] !== undefined){
          status = config.affectedGrammars[i];
        }

        if (status){
          enableGrammarsArray.push(v.value);
        }
      });

      enabledGrammars = enableGrammarsArray.join(", ");
*/
      var enableObjectsArray = [];

      _.each(Semanticolor.config.affectedSourceObjects.properties, function(v, i){
        var status = v.default;

        if (config.affectedSourceObjects[i] !== undefined){
          status = config.affectedSourceObjects[i];
        }

        if (status && v.type == 'boolean'){
          enableObjectsArray.push(v.value);
        }
      });

      enabledSourceObjects = enableObjectsArray.join(", ");

      if (config['colorDiversity']){
        colorDiversity = config['colorDiversity'];
      }

      if (updateStylesheet)
        this.updateLESSStylesheet();

      return true;
    },
    activate: function(state) {
      this.updateConfigRuntime();

      return atom.workspace.observeTextEditors(function(editor) {

        var _editor;
        _editor = editor;

        var shadow, view;

        view = $(atom.views.getView(_editor));
        shadow = $(view[0].shadowRoot);

        var _this = this;
        if (this.deactivated) return;
        var colorBounce = _.debounce(function(){
          if (this.deactivated) return;
          atom.packages.getActivePackage('semanticolor').mainModule.updateColors(shadow.find('.line'));
        }.bind(this), 10);

        atom.config.onDidChange('semanticolor', function(nv, ov){
          if (this.deactivated) return;
          _.debounce(function(){
              atom.packages.getActivePackage('semanticolor').mainModule.updateConfigRuntime(true);
          }, 1000)();
        });

        editor.onDidChangeScrollTop(function(){
          colorBounce.call(_this);
        });
        editor.onDidStopChanging(function(){
          colorBounce.call(_this);
        });
        editor.onDidChangeCursorPosition(function(positions){
          colorBounce.call(_this);
        });
        return editor.onDidChange(function() {
          colorBounce.call(_this);
        });
      });
    },
    running: false,
    confirmReload: function(){
      if (this.running) return;
      this.running = true;
      this.semanticolorView = new SemanticolorView();
      this.modalPanel = atom.workspace.addModalPanel({
        item: this.semanticolorView.getElement(),
        visible: true
      });
    },
    deactivate: function() {
      this.deactivated = true;
      this.modalPanel.destroy();
      this.subscriptions.dispose();
      return this.semanticolorView.destroy();
    },
    toggle: function(){
      this.confirmReload();
    },
    serialize: function() {
      return {
        semanticolorViewState: this.semanticolorView.serialize()
      };
    }
  };

}).call(this);
<commit_msg>update settings text<commit_after>(function() {
  var Semanticolor, SemanticolorView, CompositeDisposable;

  SemanticolorView = require('./semanticolor-view');

  CompositeDisposable = require('atom').CompositeDisposable;

  var $ = require("atom-space-pen-views").$;
  var _ = require('lodash');

  var fs = require('fs');
  var path = require('path');

  var thisDir = path.dirname(__dirname);
  var lessFile = thisDir + '/styles/semanticolor.less';

  function hash(string) {
    return string.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
    var chr, hash, i, len;
    hash = 0;
    if (string.length === 0) {
      return hash;
    }
    i = 0;
    len = string.length;
    while (i < len) {
      chr = string.charCodeAt(i);
      hash = ((hash << 5) - hash) + chr;
      hash |= 0;
      i++;
    }
    return hash;
  }

  function colorIndex(string) {
    return (Math.abs(hash(string)) % colorDiversity) + 1;
  }

  var enabledGrammars = ".php";
  var enabledSourceObjects = ".variable, .function, .function-call";
  var colorDiversity = 96;
  var excludeSourceSelector = ".comment";
  var updatedCd = null;

  var affectedSourceSelector = ".variable, .function, .function-call, .entity, .class";
  module.exports = Semanticolor = {
    config: {
  		/*affectedGrammars: {
  			type: 'object',
  			properties: {
          enablePHP: {
            title: 'PHP Enabled',
            description: "Enable the PHP source selector \'.php\'",
            type: 'boolean',
            value: '.php',
            default: true
          },
          enableJavaScript: {
            title: 'JavaScript Enabled',
            description: "Enable the PHP source selector \'.js\' (Since Atom does not parse JS proper you need the language-javascript-semantic package)",
            type: 'boolean',
            value: '.js',
            default: true
          },
          enablePython: {
            title: 'Python Enabled',
            description: "Enable the PHP source selector \'.python\'",
            type: 'boolean',
            value: '.python',
            default: true
          },
          enableCoffeeScript: {
            title: 'CoffeeScript Enabled',
            description: "Enable the CoffeeScript source selector \'.coffee\'",
            type: 'boolean',
            value: '.coffee',
            default: true
          },
          enableHTML: {
            title: 'HTML Enabled',
            description: "Enable the HTML source selector \'.html\'",
            type: 'boolean',
            value: '.html',
            default: true
          },
          additionalGrammarSelectors: {
            title: 'Define additional Grammars by their selector',
            description: "Usually along the lines of \'.somelanguage\'",
            type: 'string',
            default: ''
          }
        }
  		},*/
      affectedSourceObjects: {
        title: 'Source objects to colorize',
        description: "What grammars you want to colorize. All objects of the same name are colored the same throughout",
  			type: 'object',
  			properties: {
          enableVariables: {
            title: 'Variables Enabled',
            description: "Enable variable selector \'.variable\'",
            type: 'boolean',
            default: true,
            value: '.variable'
          },
          enableFunctions: {
            title: 'Functions Enabled',
            description: "Enable function selector \'.function\'",
            type: 'boolean',
            default: true,
            value: '.function'
          },
          enableFunctionCalls: {
            title: 'Function Calls Enabled',
            description: "Enable function calls selector \'.function-call\'",
            type: 'boolean',
            default: true,
            value: '.function-call'
          },
          enableEntities: {
            title: 'Class Entities',
            description: "Enable entity selector \'.entity\'",
            type: 'boolean',
            default: true,
            value: '.entity'
          },
          enableClasses: {
            title: 'Class Names',
            description: "Enable class selector \'.class\'",
            type: 'boolean',
            default: true,
            value: '.class'
          },
          enableConstants: {
            title: 'Constants',
            description: "Enable constants selector \'.constant\'",
            type: 'boolean',
            default: true,
            value: '.constant'
          },
          enableHtmlTags: {
            title: 'HTML Tags',
            description: "Enable function selector \'.block\'",
            type: 'boolean',
            default: true,
            value: '.block'
          },
          additionalSelectors: {
            title: 'Define additional selectors for colorizing. All similar innerTexts are the same color.',
            description: "Usually along the lines of \'.comment, .example\'. Must be comma delimited!",
            type: 'string',
            default: ''
          }
        }
  		},
  		/*affectedSourceSelector: {
  			type: 'string',
  			'default': ".variable, .function, .function-call, .entity, .class, .block.html"
  		},*/
  		excludeSourceSelector: {
  			type: 'string',
  			'default': '.comment'
  		},
      colorDiversity: {
        type: 'integer',
        text: 'Affects how diverse the colors are.',
        description: 'Higher number will allow for more unique tokens and colors. Lower will limit the colors used.',
        minimum: 8,
        default: 96,
        maximum: 360
      },
      saturationLevelA1: {
        type: 'integer',
        text: 'Used for generating the additional colors using @hsl',
        description: 'See README on https://atom.io/packages/semanticolor',
        minimum: 0,
        default: 100,
        maximum: 100
      },
      saturationLevelA2: {
        type: 'integer',
        text: 'Used for generating the additional colors using @hsl',
        description: 'See README on https://atom.io/packages/semanticolor',
        minimum: 0,
        default: 10,
        maximum: 100
      },
      saturationLevelB1: {
        type: 'integer',
        text: 'Used for generating the additional colors using @hsl',
        description: 'See README on https://atom.io/packages/semanticolor',
        minimum: 0,
        default: 75,
        maximum: 100
      },
      saturationLevelB2: {
        type: 'integer',
        text: 'Used for generating the additional colors using @hsl',
        description: 'See README on https://atom.io/packages/semanticolor',
        minimum: 0,
        default: 75,
        maximum: 100
      }
  	},
    semanticolorView: SemanticolorView,
    modalPanel: SemanticolorView,
    selector: '.source',
    disableForSelector: '',
    subscriptions: null,
    updateColors: function(loc){
      loc.find(enabledSourceObjects).each(function(i, el) {
        if ($(el).is(excludeSourceSelector)) return;
        /*if (!$(el).is(enabledGrammars)) return;*/
        var text = el.innerText;
        var semanticClass = "identifier semanticolor-" + colorIndex(text);
        $(el).addClass(semanticClass);
      });
    },
    updateLESSStylesheet: function(){
      var cd = atom.config.get('semanticolor').colorDiversity;
      var sla1 = atom.config.get('semanticolor').saturationLevelA1 || 100;
      var sla2 = atom.config.get('semanticolor').saturationLevelA2 || 10;
      var slb1 = atom.config.get('semanticolor').saturationLevelB1 || 75;
      var slb2 = atom.config.get('semanticolor').saturationLevelB2 || 75;
      /*var lessUpdate = "@colorDiversity: " + cd + ';';*/
      var lessUpdate = [
        "@colorDiversity: "+cd+";",
        "@saturationLevelA1: "+sla1+"%;",
        "@saturationLevelA2: "+sla2+"%;",
        "@saturationLevelB1: "+slb1+"%;",
        "@saturationLevelB2: "+slb2+"%;"
      ].join("\n");

      /*var lessUpdate = lessUpdateArray.join("\n");*/
      console.log(lessUpdate);
      var written = false;
      try {
        var currentLess = fs.readFileSync(lessFile);
        if (currentLess != lessUpdate){
          fs.writeFileSync(lessFile, lessUpdate);
        }
        atom.notifications.addSuccess('Rewrote new colorDiversity...',{detail: 'Reloading with stylesheet of {'+colorDiversity+'} possible colors.'});
        this.confirmReload();
      } catch(e) {
        if (e.code === 'ENOENT') {
            atom.notifications.addError('No initial colorDiversity configured...',{detail: 'Updating stylesheet with default of {'+colorDiversity+'} possible colors.'});
            try {
              // var lessUpdate = "@colorDiversity: " + 96 + ';';
              var lessUpdate = [
                "@colorDiversity: 96;",
                "@saturationLevelA1: 100%;",
                "@saturationLevelA2: 10%;",
                "@saturationLevelB1: 75%;",
                "@saturationLevelB2: 75%;"
              ].join("\n");
              written = fs.writeFileSync(lessFile, lessUpdate);
            } catch(err) {
              console.log(err);
              atom.notifications.addError(e.code + ' : ' + e.message, {detail: 'Something failed not sure what. Open an issue with me!'});
            }
          } else {
            atom.notifications.addError('No initial colorDiversity configured...',{detail: 'Updating stylesheet with default of {'+colorDiversity+'} possible colors.'});
          }
      } finally {

      }
    },
    updateConfigRuntime: function(updateStylesheet){
      var config = atom.config.get('semanticolor');

      /*var enableGrammarsArray = [];
      _.each(Semanticolor.config.affectedGrammars.properties, function(v, i){
        var status = v.default;

        if (config.affectedGrammars[i] !== undefined){
          status = config.affectedGrammars[i];
        }

        if (status){
          enableGrammarsArray.push(v.value);
        }
      });

      enabledGrammars = enableGrammarsArray.join(", ");
*/
      var enableObjectsArray = [];

      _.each(Semanticolor.config.affectedSourceObjects.properties, function(v, i){
        var status = v.default;

        if (config.affectedSourceObjects[i] !== undefined){
          status = config.affectedSourceObjects[i];
        }

        if (status && v.type == 'boolean'){
          enableObjectsArray.push(v.value);
        }
      });

      enabledSourceObjects = enableObjectsArray.join(", ");

      if (config['colorDiversity']){
        colorDiversity = config['colorDiversity'];
      }

      if (updateStylesheet)
        this.updateLESSStylesheet();

      return true;
    },
    activate: function(state) {
      this.updateConfigRuntime();

      return atom.workspace.observeTextEditors(function(editor) {

        var _editor;
        _editor = editor;

        var shadow, view;

        view = $(atom.views.getView(_editor));
        shadow = $(view[0].shadowRoot);

        var _this = this;
        if (this.deactivated) return;
        var colorBounce = _.debounce(function(){
          if (this.deactivated) return;
          atom.packages.getActivePackage('semanticolor').mainModule.updateColors(shadow.find('.line'));
        }.bind(this), 10);

        atom.config.onDidChange('semanticolor', function(nv, ov){
          if (this.deactivated) return;
          _.debounce(function(){
              atom.packages.getActivePackage('semanticolor').mainModule.updateConfigRuntime(true);
          }, 1000)();
        });

        editor.onDidChangeScrollTop(function(){
          colorBounce.call(_this);
        });
        editor.onDidStopChanging(function(){
          colorBounce.call(_this);
        });
        editor.onDidChangeCursorPosition(function(positions){
          colorBounce.call(_this);
        });
        return editor.onDidChange(function() {
          colorBounce.call(_this);
        });
      });
    },
    running: false,
    confirmReload: function(){
      if (this.running) return;
      this.running = true;
      this.semanticolorView = new SemanticolorView();
      this.modalPanel = atom.workspace.addModalPanel({
        item: this.semanticolorView.getElement(),
        visible: true
      });
    },
    deactivate: function() {
      this.deactivated = true;
      this.modalPanel.destroy();
      this.subscriptions.dispose();
      return this.semanticolorView.destroy();
    },
    toggle: function(){
      this.confirmReload();
    },
    serialize: function() {
      return {
        semanticolorViewState: this.semanticolorView.serialize()
      };
    }
  };

}).call(this);
<|endoftext|>


<commit_before>import React from 'react';
import $ from 'jquery';
import makeMorris from 'morris-js-module';

import { formatMoney } from '../utils';

var Morris = makeMorris($);

class Currencies extends React.Component {
  constructor() {
    super();

    fetch('/currencies?currency=EUR').then(response => {
      response.json().then(funds => {
        funds = funds.map(h => {
          return { label: h.amount_currency, value: h.amount };
        });

        Morris.Donut({
          element: 'morris-donut-chart-ws',
          data: funds,
          formatter: (y, data) => {
            return formatMoney(y, 'EUR');
          },
          resize: true
        });
      });
    });
  }

  render() {
    return(

 Currencies

    );
  }
}

export default Currencies;
<commit_msg>src: show total amount of savings under Currencies<commit_after>import React from 'react';
import $ from 'jquery';
import makeMorris from 'morris-js-module';

import { formatMoney } from '../utils';

var Morris = makeMorris($);

class Currencies extends React.Component {
  constructor() {
    super();

    this.state = {
      total_amount: 0.0
    };

    fetch('/currencies?currency=EUR').then(response => {
      response.json().then(funds => {
        let total_amount = funds.map(h => {
          return h.amount;
        }).reduce((a, b) => {
          return a + b;
        });

        this.setState({ total_amount: total_amount });

        funds = funds.map(h => {
          return { label: h.amount_currency, value: h.amount };
        });

        Morris.Donut({
          element: 'morris-donut-chart-ws',
          data: funds,
          formatter: (y, data) => {
            return formatMoney(y, 'EUR');
          },
          resize: true
        });
      });
    });
  }

  render() {
    return(

 Currencies

              {formatMoney(this.state.total_amount, 'EUR')}

    );
  }
}

export default Currencies;
<|endoftext|>


<commit_before>'use strict';

import React from "react/addons";
var MapComponent = require('components/Map');
var Modal = require('components/Modal');
var CategoriesAction = require('actions/CategoriesActionCreators');
var GeoObjectsAction = require('actions/GeoObjectActionCreators');
var GeoListTable = require('reactable').Table;
require('styles/GeoDetails.less');

var GeoDetails = React.createClass({

    /**
     * Click handler for new category prompt
     * @param event
     */
    handlePromptNewCategory: function (event) {
        this.refs.addCategoryModalForm.show();
        event.preventDefault();
    },

    /**
     * Add new category form cancel button handler
     * @param event
     */
    onCancelNewCategory: function (event) {
        this.refs.addCategoryModalForm.hide();
        event.preventDefault();
    },

    /**
     * Create new category with raw data
     * @param event
     */
    onAddNewCategoryPrompt: function (event) {
        CategoriesAction.addOneCategory({
            name: this.refs.newCategoryName.getDOMNode().value
        });
        event.preventDefault();
    },

    /**
     * Click handler for new geo object
     * @param event
     */
    showGeoObjectFormObject: function (event) {
        CategoriesAction.addOneCategory({
            name: this.refs.addGeoObjectModalForm.show()
        });
        event.preventDefault();
    },

    /**
     * Create new geo object with raw data
     * @param event
     */
    onAddNewGeoObjectPrompt: function (event) {
        GeoObjectsAction.addOneObject({
            name: this.refs.newGeoObjectName.getDOMNode().value,
            addres: this.refs.newGeoObjectAddress.getDOMNode().value,
            latitude: this.refs.newGeoObjectLatitude.getDOMNode().value,
            longitude: this.refs.newGeoObjectLongitude.getDOMNode().value,
            category: this.refs.newGeoObjectCategory.getDOMNode().value
        });
        event.preventDefault();
    },

    /**
     * Add new geo object form cancel button handler
     * @param event
     */
    onCancelNewGeoObject: function (event) {
        this.refs.addGeoObjectModalForm.hide();
        event.preventDefault();
    },

    render: function () {
        return (

                        <input type="radio" id="tab-map" name="tab-group-2" defaultChecked/>
                        Карта

                            <MapComponent />

                        <input type="radio" id="tab-list" name="tab-group-2"/>
                        Список

                                Новая категория
                                Новый объект

                            <GeoListTable className="geoTable" data={[

                            ]}/>

                <Modal visible={false} closable={true} ref="addCategoryModalForm">

Новая категория

                        <form className="modalForm">

                                Имя
                                <input type="text" name="name" size="25" ref="newCategoryName"/>

                                    <input type="button" value="Создать категорию" onClick={this.onAddNewCategoryPrompt}/>
                                    <input type="button" value="Отмена" onClick={this.onCancelNewCategory}/>

                        </form>

                </Modal>
                <Modal visible={false} closable={true} ref="addGeoObjectModalForm">

Новый объект

                    <form name="newGeoObject">

                                Имя
                                <input type="text" name="name" size="25" ref="newGeoObjectName"/>

                                Адрес
                                <input type="text" name="address" size="25" ref="newGeoObjectAddress"/>

                                Широта

                                    <input type="text" name="latitude" size="25" ref="newGeoObjectLatitude"/>

                                Долгота

                                    <input type="text" name="longitude" size="25" ref="newGeoObjectLongitude"/>

                                Категория

                                    <select name="category" ref="newGeoObjectCategory">
                                        <option defaultSelected value="empty">Без категории</option>
                                    </select>

                                Карта

                                    <MapComponent />

                                    <input type="button" value="Создать объект" onClick={this.onAddNewGeoObjectPrompt}/>
                                    <input type="button" value="Отмена" onClick={this.onCancelNewGeoObject} />

                    </form>
                </Modal>

        );
    }
});

module.exports = GeoDetails;

<commit_msg>show geo objects into table<commit_after>'use strict';

import React from "react/addons";
var MapComponent = require('components/Map');
var Modal = require('components/Modal');
var CategoriesAction = require('actions/CategoriesActionCreators');
var GeoObjectsAction = require('actions/GeoObjectActionCreators');
var GeoListTable = require('reactable').Table;
var _ = require('underscore');

require('styles/GeoDetails.less');

var GeoDetails = React.createClass({

    /**
     * Click handler for new category prompt
     * @param event
     */
    handlePromptNewCategory: function (event) {
        this.refs.addCategoryModalForm.show();
        event.preventDefault();
    },

    /**
     * Add new category form cancel button handler
     * @param event
     */
    onCancelNewCategory: function (event) {
        this.refs.addCategoryModalForm.hide();
        event.preventDefault();
    },

    /**
     * Create new category with raw data
     * @param event
     */
    onAddNewCategoryPrompt: function (event) {
        CategoriesAction.addOneCategory({
            name: this.refs.newCategoryName.getDOMNode().value
        });
        event.preventDefault();
    },

    /**
     * Click handler for new geo object
     * @param event
     */
    showGeoObjectFormObject: function (event) {
        CategoriesAction.addOneCategory({
            name: this.refs.addGeoObjectModalForm.show()
        });
        event.preventDefault();
    },

    /**
     * Create new geo object with raw data
     * @param event
     */
    onAddNewGeoObjectPrompt: function (event) {
        GeoObjectsAction.addOneObject({
            name: this.refs.newGeoObjectName.getDOMNode().value,
            addres: this.refs.newGeoObjectAddress.getDOMNode().value,
            latitude: this.refs.newGeoObjectLatitude.getDOMNode().value,
            longitude: this.refs.newGeoObjectLongitude.getDOMNode().value,
            category: this.refs.newGeoObjectCategory.getDOMNode().value
        });
        event.preventDefault();
    },

    /**
     * Add new geo object form cancel button handler
     * @param event
     */
    onCancelNewGeoObject: function (event) {
        this.refs.addGeoObjectModalForm.hide();
        event.preventDefault();
    },

    render: function () {
        return (

                        <input type="radio" id="tab-map" name="tab-group-2" defaultChecked/>
                        Карта

                            <MapComponent />

                        <input type="radio" id="tab-list" name="tab-group-2"/>
                        Список

                                Новая категория
                                Новый объект

                            <GeoListTable className="geoTable" data={_.toArray(this.props.geoObjects)}/>

                <Modal visible={false} closable={true} ref="addCategoryModalForm">

Новая категория

                        <form className="modalForm">

                                Имя
                                <input type="text" name="name" size="25" ref="newCategoryName"/>

                                    <input type="button" value="Создать категорию" onClick={this.onAddNewCategoryPrompt}/>
                                    <input type="button" value="Отмена" onClick={this.onCancelNewCategory}/>

                        </form>

                </Modal>
                <Modal visible={false} closable={true} ref="addGeoObjectModalForm">

Новый объект

                    <form name="newGeoObject">

                                Имя
                                <input type="text" name="name" size="25" ref="newGeoObjectName"/>

                                Адрес
                                <input type="text" name="address" size="25" ref="newGeoObjectAddress"/>

                                Широта

                                    <input type="text" name="latitude" size="25" ref="newGeoObjectLatitude"/>

                                Долгота

                                    <input type="text" name="longitude" size="25" ref="newGeoObjectLongitude"/>

                                Категория

                                    <select name="category" ref="newGeoObjectCategory">
                                        <option defaultSelected value="empty">Без категории</option>
                                    </select>

                                Карта

                                    <MapComponent />

                                    <input type="button" value="Создать объект" onClick={this.onAddNewGeoObjectPrompt}/>
                                    <input type="button" value="Отмена" onClick={this.onCancelNewGeoObject} />

                    </form>
                </Modal>

        );
    }
});

module.exports = GeoDetails;

<|endoftext|>


<commit_before>const icons = {
  arrowBack: 'M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z',
  menu: 'M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z',
  info: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z',
  code: 'M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z',
  assessment: 'M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z',
  groupAdd: 'M8 10H5V7H3v3H0v2h3v3h2v-3h3v-2zm10 1c1.66 0 2.99-1.34 2.99-3S19.66 5 18 5c-.32 0-.63.05-.91.14.57.81.9 1.79.9 2.86s-.34 2.04-.9 2.86c.28.09.59.14.91.14zm-5 0c1.66 0 2.99-1.34 2.99-3S14.66 5 13 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm6.62 2.16c.83.73 1.38 1.66 1.38 2.84v2h3v-2c0-1.54-2.37-2.49-4.38-2.84zM13 13c-2 0-6 1-6 3v2h12v-2c0-2-4-3-6-3z'
};

export default icons;
<commit_msg>chore: remove "groupAdd" icon<commit_after>const icons = {
  arrowBack: 'M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z',
  menu: 'M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z',
  info: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z',
  code: 'M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z',
  assessment: 'M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z'
};

export default icons;
<|endoftext|>


<commit_before>var STATUS_CODES = require('http').STATUS_CODES || {};
var TYPES = ['whistle', 'Fiddler'];

function getMethod(method) {
  if (typeof method !== 'string') {
    return 'GET';
  }
  return method.toUpperCase() || 'GET';
}

function getHeadersRaw(headers) {
  if (!headers) {
    return [];
  }
  delete headers.Host;
  return Object.keys(headers).map(function(name) {
    return name + ': ' + headers[name];
  });
}

function getReqRaw(req) {
  var headers = getHeadersRaw(req.headers);
  var url = String(req.url || '').replace(/^ws/, 'http');
  headers.unshift([getMethod(req.method), url, 'HTTP/1.1'].join(' '));
  return headers.join('\r\n') + '\r\n\r\n' + (req.body || '');
}

exports.getReqRaw = getReqRaw;

function getResRaw(res) {
  var headers = getHeadersRaw(res.headers);
  var statusCode = /^\d+$/.test(res.statusCode) ? res.statusCode : 0;
  headers.unshift(['HTTP/1.1', res.statusCode, res.statusMessage || STATUS_CODES[res.statusCode] || 'unknown'].join(' '));
  return headers.join('\r\n') + '\r\n\r\n' + (res.body || '');
}

exports.getResRaw = getResRaw;

var BODY_SEP = ['\r\n\r\n', '\r\n\r', '\r\r\n', '\r\n\n', '\r\r', '\r\n', '\n\n', '\n\r'];

function getBodyOffset(raw) {
  for (var i = 0; i < BODY_SEP.length; i++) {
    var sep = BODY_SEP[i];
    var index = raw.indexOf(sep);
    if (index !== -1) {
      return [index, index + sep.length];
    }
  }
}

function foratRawData(raw) {
  if (typeof raw !== 'string' || !(raw = raw.trim())) {
    return;
  }
  var offset = getBodyOffset(raw);
  var body = '';
  if (offset) {
    body = raw.substring(offset[1]).replace(BODY_SEP, '');
    raw = raw.substring(0, offset[0]);
  }
  raw = raw.trim().split(/\r\n?|\n/g);
  var firstLine = raw.shift().split(/\s+/g);
  var headers = {};
  raw.forEach(function(line) {
    var index = line.indexOf(':');
    if (index === -1) {
      return;
    }
    var name = line.substring(0, index).trim();
    if (!name) {
      return;
    }
    var value = headers[name];
    var val = line.substring(index + 1).trim();
    if (value != null) {
      if (Array.isArray(value)) {
        value.push(val);
      } else {
        value = [value, val];
      }
    } else {
      value = val;
    }
    headers[name.toLowerCase()] = value;
  });

  return {
    firstLine: firstLine,
    headers: headers,
    body: body
  };
}

function getReq(raw) {
  raw = foratRawData(raw);
  return raw ? {
    method: raw.firstLine[0],
    httpVersion: '1.1',
    url: raw.firstLine[1],
    headers: raw.headers,
    body: raw.body
  } : null;
}

exports.getReq = getReq;

function getRes(raw) {
  raw = foratRawData(raw);
  return raw ? {
    statusCode: raw.firstLine[1],
    httpVersion: '1.1',
    statusMessage: raw.firstLine[2],
    headers: raw.headers,
    body: raw.body
  } : {};
}

exports.getRes = getRes;

function parseJSON(str) {
  try {
    return JSON.parse(str);
  } catch(e) {}
}

exports.parseJSON = parseJSON;

function padding(num) {
  return num < 10 ? '0' + num : num;
}

function getFilename(type) {
  if (!~TYPES.indexOf(type)) {
    type = 'whistle';
  }
  var date = new Date();
  var filename = [date.getFullYear(), padding(date.getMonth() + 1), padding(date.getDate())].join('-')
    + '_' + [padding(date.getHours()), padding(date.getMinutes()), padding(date.getSeconds())].join('-');
  return filename + (type === 'whistle' ? '.txt' : '.saz');
}

exports.getFilename = getFilename;

var ONE_MINUTE = 60 * 1000;
function toISOString(time) {
  var date = new Date();
  var offet = -date.getTimezoneOffset();
  time += offet * ONE_MINUTE;
  offet /= 60;
  time = time >= 0 ? new Date(time) : new Date();
  return time.toISOString().slice(0, -1) + '0000'
    + (offet >= 0 ? '+' : '-') + padding(Math.abs(offet)) + ':00';
}

exports.toISOString = toISOString;
<commit_msg>feat: Export the saz file<commit_after>var STATUS_CODES = require('http').STATUS_CODES || {};
var TYPES = ['whistle', 'Fiddler'];

function getMethod(method) {
  if (typeof method !== 'string') {
    return 'GET';
  }
  return method.toUpperCase() || 'GET';
}

function getHeadersRaw(headers) {
  if (!headers) {
    return [];
  }
  delete headers.Host;
  return Object.keys(headers).map(function(name) {
    return name + ': ' + headers[name];
  });
}

function getReqRaw(req) {
  var headers = getHeadersRaw(req.headers);
  var url = String(req.url || '').replace(/^ws/, 'http');
  headers.unshift([getMethod(req.method), url, 'HTTP/1.1'].join(' '));
  return headers.join('\r\n') + '\r\n\r\n' + (req.body || '');
}

exports.getReqRaw = getReqRaw;

function getResRaw(res) {
  var headers = getHeadersRaw(res.headers);
  var statusCode = /^\d+$/.test(res.statusCode) ? res.statusCode : 0;
  headers.unshift(['HTTP/1.1', statusCode, res.statusMessage || STATUS_CODES[statusCode] || 'unknown'].join(' '));
  return headers.join('\r\n') + '\r\n\r\n' + (res.body || '');
}

exports.getResRaw = getResRaw;

var BODY_SEP = ['\r\n\r\n', '\r\n\r', '\r\r\n', '\r\n\n', '\r\r', '\r\n', '\n\n', '\n\r'];

function getBodyOffset(raw) {
  for (var i = 0; i < BODY_SEP.length; i++) {
    var sep = BODY_SEP[i];
    var index = raw.indexOf(sep);
    if (index !== -1) {
      return [index, index + sep.length];
    }
  }
}

function foratRawData(raw) {
  if (typeof raw !== 'string' || !(raw = raw.trim())) {
    return;
  }
  var offset = getBodyOffset(raw);
  var body = '';
  if (offset) {
    body = raw.substring(offset[1]).replace(BODY_SEP, '');
    raw = raw.substring(0, offset[0]);
  }
  raw = raw.trim().split(/\r\n?|\n/g);
  var firstLine = raw.shift().split(/\s+/g);
  var headers = {};
  raw.forEach(function(line) {
    var index = line.indexOf(':');
    if (index === -1) {
      return;
    }
    var name = line.substring(0, index).trim();
    if (!name) {
      return;
    }
    var value = headers[name];
    var val = line.substring(index + 1).trim();
    if (value != null) {
      if (Array.isArray(value)) {
        value.push(val);
      } else {
        value = [value, val];
      }
    } else {
      value = val;
    }
    headers[name.toLowerCase()] = value;
  });

  return {
    firstLine: firstLine,
    headers: headers,
    body: body
  };
}

function getReq(raw) {
  raw = foratRawData(raw);
  return raw ? {
    method: raw.firstLine[0],
    httpVersion: '1.1',
    url: raw.firstLine[1],
    headers: raw.headers,
    body: raw.body
  } : null;
}

exports.getReq = getReq;

function getRes(raw) {
  raw = foratRawData(raw);
  return raw ? {
    statusCode: raw.firstLine[1],
    httpVersion: '1.1',
    statusMessage: raw.firstLine[2],
    headers: raw.headers,
    body: raw.body
  } : {};
}

exports.getRes = getRes;

function parseJSON(str) {
  try {
    return JSON.parse(str);
  } catch(e) {}
}

exports.parseJSON = parseJSON;

function padding(num) {
  return num < 10 ? '0' + num : num;
}

function getFilename(type) {
  if (!~TYPES.indexOf(type)) {
    type = 'whistle';
  }
  var date = new Date();
  var filename = [date.getFullYear(), padding(date.getMonth() + 1), padding(date.getDate())].join('-')
    + '_' + [padding(date.getHours()), padding(date.getMinutes()), padding(date.getSeconds())].join('-');
  return filename + (type === 'whistle' ? '.txt' : '.saz');
}

exports.getFilename = getFilename;

var ONE_MINUTE = 60 * 1000;
function toISOString(time) {
  var date = new Date();
  var offet = -date.getTimezoneOffset();
  time += offet * ONE_MINUTE;
  offet /= 60;
  time = time >= 0 ? new Date(time) : new Date();
  return time.toISOString().slice(0, -1) + '0000'
    + (offet >= 0 ? '+' : '-') + padding(Math.abs(offet)) + ':00';
}

exports.toISOString = toISOString;
<|endoftext|>