<commit_before>var _ = require('../util')
var config = require('../config')
var textParser = require('../parse/text')
var dirParser = require('../parse/directive')
var templateParser = require('../parse/template')

function noop () {}

/**
 * Compile a template and return a reusable composite link
 * function, which recursively contains more link functions
 * inside. This top level compile function should only be
 * called on instance root nodes.
 *
 * @param {Element|DocumentFragment} el
 * @param {Object} options
 * @param {Boolean} partial
 * @return {Function}
 */

module.exports = function compile (el, options, partial) {
  var params = !partial && options.paramAttributes
  var paramsLinkFn = params
    ? compileParamAttributes(el, params, options)
    : null
  var nodeLinkFn = el instanceof DocumentFragment
    ? null
    : compileNode(el, options)
  var childLinkFn =
    (!nodeLinkFn || !nodeLinkFn.terminal) &&
    el.hasChildNodes()
      ? compileNodeList(el.childNodes, options)
      : null
  return function link (vm, el) {
    if (paramsLinkFn) paramsLinkFn(vm, el)
    if (nodeLinkFn) nodeLinkFn(vm, el)
    if (childLinkFn) childLinkFn(vm, el.childNodes)
  }
}

/**
 * Compile a node and return a nodeLinkFn based on the
 * node type.
 *
 * @param {Node} node
 * @param {Object} options
 * @return {Function|undefined}
 */

function compileNode (node, options) {
  var type = node.nodeType
  if (type === 1 && node.tagName !== 'SCRIPT') {
    return compileElement(node, options)
  } else if (type === 3 && config.interpolate) {
    return compileTextNode(node, options)
  }
}

/**
 * Compile an element and return a nodeLinkFn.
 *
 * @param {Element} el
 * @param {Object} options
 * @return {Function|null}
 */

function compileElement (el, options) {
  var hasAttributes = el.hasAttributes()
  var tag = el.tagName.toLowerCase()
  if (hasAttributes) {
    // check terminal direcitves
    var terminalLinkFn
    for (var i = 0; i < 3; i++) {
      terminalLinkFn = checkTerminalDirectives(el, options)
      if (terminalLinkFn) {
        terminalLinkFn.terminal = true
        return terminalLinkFn
      }
    }
  }
  // check custom element component
  var component =
    tag.indexOf('-') > 0 &&
    options.components[tag]
  if (component) {
    return makeTeriminalLinkFn(el, 'component', tag, options)
  }
  // check other directives
  var linkFn
  if (hasAttributes) {
    var directives = collectDirectives(el, options)
    linkFn = directives.length
      ? makeDirectivesLinkFn(directives)
      : null
  }
  // if the element is a textarea, we need to interpolate
  // its content on initial render.
  if (el.tagName === 'TEXTAREA') {
    var realLinkFn = linkFn
    linkFn = function (vm, el) {
      el.value = vm.$interpolate(el.value)
      if (realLinkFn) realLinkFn(vm, el)
    }
  }
  return linkFn
}

/**
 * Build a multi-directive link function.
 *
 * @param {Array} directives
 * @return {Function} directivesLinkFn
 */

function makeDirectivesLinkFn (directives) {
  return function directivesLinkFn (vm, el) {
    // reverse apply because it's sorted low to high
    var i = directives.length
    var dir, j
    while (i--) {
      dir = directives[i]
      if (dir._link) {
        // custom link fn
        dir._link(vm, el)
      } else {
        j = dir.descriptors.length
        while (j--) {
          vm._bindDir(dir.name, el,
                      dir.descriptors[j], dir.def)
        }
      }
    }
  }
}

/**
 * Compile a textNode and return a nodeLinkFn.
 *
 * @param {TextNode} node
 * @param {Object} options
 * @return {Function|null} textNodeLinkFn
 */

function compileTextNode (node, options) {
  var tokens = textParser.parse(node.nodeValue)
  if (!tokens) {
    return null
  }
  var frag = document.createDocumentFragment()
  var dirs = options.directives
  var el, token, value
  for (var i = 0, l = tokens.length; i < l; i++) {
    token = tokens[i]
    value = token.value
    if (token.tag) {
      if (token.oneTime) {
        el = document.createTextNode(value)
      } else {
        if (token.html) {
          el = document.createComment('v-html')
          token.type = 'html'
          token.def = dirs.html
          token.descriptor = dirParser.parse(value)[0]
        } else if (token.partial) {
          el = document.createComment('v-partial')
          token.type = 'partial'
          token.def = dirs.partial
          token.descriptor = dirParser.parse(value)[0]
        } else {
          // IE will clean up empty textNodes during
          // frag.cloneNode(true), so we have to give it
          // something here...
          el = document.createTextNode(' ')
          token.type = 'text'
          token.def = dirs.text
          token.descriptor = dirParser.parse(value)[0]
        }
      }
    } else {
      el = document.createTextNode(value)
    }
    frag.appendChild(el)
  }
  return makeTextNodeLinkFn(tokens, frag, options)
}

/**
 * Build a function that processes a textNode.
 *
 * @param {Array<Object>} tokens
 * @param {DocumentFragment} frag
 */

function makeTextNodeLinkFn (tokens, frag) {
  return function textNodeLinkFn (vm, el) {
    var fragClone = frag.cloneNode(true)
    var childNodes = _.toArray(fragClone.childNodes)
    var token, value, node
    for (var i = 0, l = tokens.length; i < l; i++) {
      token = tokens[i]
      value = token.value
      if (token.tag) {
        node = childNodes[i]
        if (token.oneTime) {
          value = vm.$eval(value)
          if (token.html) {
            _.replace(node, templateParser.parse(value, true))
          } else {
            node.nodeValue = value
          }
        } else {
          vm._bindDir(token.type, node,
                      token.descriptor, token.def)
        }
      }
    }
    _.replace(el, fragClone)
  }
}

/**
 * Compile a node list and return a childLinkFn.
 *
 * @param {NodeList} nodeList
 * @param {Object} options
 * @return {Function|undefined}
 */

function compileNodeList (nodeList, options) {
  var linkFns = []
  var nodeLinkFn, childLinkFn, node
  for (var i = 0, l = nodeList.length; i < l; i++) {
    node = nodeList[i]
    nodeLinkFn = compileNode(node, options)
    childLinkFn =
      (!nodeLinkFn || !nodeLinkFn.terminal) &&
      node.hasChildNodes()
        ? compileNodeList(node.childNodes, options)
        : null
    linkFns.push(nodeLinkFn, childLinkFn)
  }
  return linkFns.length
    ? makeChildLinkFn(linkFns)
    : null
}

/**
 * Make a child link function for a node's childNodes.
 *
 * @param {Array<Function>} linkFns
 * @return {Function} childLinkFn
 */

function makeChildLinkFn (linkFns) {
  return function childLinkFn (vm, nodes) {
    // stablize nodes
    nodes = _.toArray(nodes)
    var node, nodeLinkFn, childrenLinkFn
    for (var i = 0, n = 0, l = linkFns.length; i < l; n++) {
      node = nodes[n]
      nodeLinkFn = linkFns[i++]
      childrenLinkFn = linkFns[i++]
      if (nodeLinkFn) {
        nodeLinkFn(vm, node)
      }
      if (childrenLinkFn) {
        childrenLinkFn(vm, node.childNodes)
      }
    }
  }
}

/**
 * Compile param attributes on a root element and return
 * a paramAttributes link function.
 *
 * @param {Element} el
 * @param {Array} attrs
 * @param {Object} options
 * @return {Function} paramsLinkFn
 */

function compileParamAttributes (el, attrs, options) {
  var params = []
  var i = attrs.length
  var name, value, param
  while (i--) {
    name = attrs[i]
    value = el.getAttribute(name)
    if (value !== null) {
      el.removeAttribute(name)
      param = {
        name: name,
        value: value
      }
      var tokens = textParser.parse(value)
      if (tokens) {
        if (tokens.length > 1) {
          _.warn(
            'Invalid param attribute binding: "' +
            name + '="' + value + '"' +
            '\nDon\'t mix binding tags with plain text ' +
            'in param attribute bindings.'
          )
          continue
        } else {
          param.dynamic = true
          param.value = tokens[0].value
        }
      }
      params.push(param)
    }
  }
  return makeParamsLinkFn(params, options)
}

/**
 * Build a function that applies param attributes to a vm.
 *
 * @param {Array} params
 * @param {Object} options
 * @return {Function} paramsLinkFn
 */

function makeParamsLinkFn (params, options) {
  var def = options.directives['with']
  return function paramsLinkFn (vm, el) {
    var i = params.length
    var param
    while (i--) {
      param = params[i]
      if (param.dynamic) {
        // dynamic param attribtues are bound as v-with.
        // we can directly duck the descriptor here beacuse
        // param attributes cannot use expressions or
        // filters.
        vm._bindDir('with', el, {
          arg: param.name,
          expression: param.value
        }, def)
      } else {
        // just set once
        vm.$set(param.name, param.value)
      }
    }
  }
}

/**
 * Check an element for terminal directives in fixed order.
 * If it finds one, return a terminal link function.
 *
 * @param {Element} el
 * @param {Object} options
 * @return {Function} terminalLinkFn
 */

var terminalDirectives = [
  'repeat',
  'component',
  'if'
]

function checkTerminalDirectives (el, options) {
  if (_.attr(el, 'pre') !== null) {
    return noop
  }
  var value, dirName
  /* jshint boss: true */
  for (var i = 0; i < 3; i++) {
    dirName = terminalDirectives[i]
    if (value = _.attr(el, dirName)) {
      return makeTeriminalLinkFn(el, dirName, value, options)
    }
  }
}

/**
 * Build a link function for a terminal directive.
 *
 * @param {Element} el
 * @param {String} dirName
 * @param {String} value
 * @param {Object} options
 * @return {Function} terminalLinkFn
 */

function makeTeriminalLinkFn (el, dirName, value, options) {
  var descriptor = dirParser.parse(value)[0]
  var def = options.directives[dirName]
  return function terminalLinkFn (vm, el) {
    vm._bindDir(dirName, el, descriptor, def)
  }
}

/**
 * Collect the directives on an element.
 *
 * @param {Element} el
 * @param {Object} options
 * @return {Array}
 */

function collectDirectives (el, options) {
  var attrs = _.toArray(el.attributes)
  var i = attrs.length
  var dirs = []
  var attr, attrName, dir, dirName, dirDef
  while (i--) {
    attr = attrs[i]
    attrName = attr.name
    if (attrName.indexOf(config.prefix) === 0) {
      dirName = attrName.slice(config.prefix.length)
      dirDef = options.directives[dirName]
      _.assertAsset(dirDef, 'directive', dirName)
      if (dirDef) {
        if (dirName !== 'cloak') {
          el.removeAttribute(attrName)
        }
        dirs.push({
          name: dirName,
          descriptors: dirParser.parse(attr.value),
          def: dirDef
        })
      }
    } else if (config.interpolate) {
      dir = collectAttrDirective(el, attrName, attr.value,
                                 options)
      if (dir) {
        dirs.push(dir)
      }
    }
  }
  // sort by priority, LOW to HIGH
  dirs.sort(directiveComparator)
  return dirs
}

/**
 * Check an attribute for potential dynamic bindings,
 * and return a directive object.
 *
 * @param {Element} el
 * @param {String} name
 * @param {String} value
 * @param {Object} options
 * @return {Object}
 */

function collectAttrDirective (el, name, value, options) {
  var tokens = textParser.parse(value)
  if (tokens) {
    var def = options.directives.attr
    var i = tokens.length
    var allOneTime = true
    while (i--) {
      var token = tokens[i]
      if (token.tag && !token.oneTime) {
        allOneTime = false
      }
    }
    return {
      def: def,
      _link: allOneTime
        ? function (vm, el) {
            el.setAttribute(name, vm.$interpolate(value))
          }
        : function (vm, el) {
            var value = textParser.tokensToExp(tokens, vm)
            var desc = dirParser.parse(name + ':' + value)[0]
            vm._bindDir('attr', el, desc, def)
          }
    }
  }
}

/**
 * Directive priority sort comparator
 *
 * @param {Object} a
 * @param {Object} b
 */

function directiveComparator (a, b) {
  a = a.def.priority || 0
  b = b.def.priority || 0
  return a > b ? 1 : -1
}<commit_msg>only remove dynamic paramAttribute bindings<commit_after>var _ = require('../util')
var config = require('../config')
var textParser = require('../parse/text')
var dirParser = require('../parse/directive')
var templateParser = require('../parse/template')

function noop () {}

/**
 * Compile a template and return a reusable composite link
 * function, which recursively contains more link functions
 * inside. This top level compile function should only be
 * called on instance root nodes.
 *
 * @param {Element|DocumentFragment} el
 * @param {Object} options
 * @param {Boolean} partial
 * @return {Function}
 */

module.exports = function compile (el, options, partial) {
  var params = !partial && options.paramAttributes
  var paramsLinkFn = params
    ? compileParamAttributes(el, params, options)
    : null
  var nodeLinkFn = el instanceof DocumentFragment
    ? null
    : compileNode(el, options)
  var childLinkFn =
    (!nodeLinkFn || !nodeLinkFn.terminal) &&
    el.hasChildNodes()
      ? compileNodeList(el.childNodes, options)
      : null
  return function link (vm, el) {
    if (paramsLinkFn) paramsLinkFn(vm, el)
    if (nodeLinkFn) nodeLinkFn(vm, el)
    if (childLinkFn) childLinkFn(vm, el.childNodes)
  }
}

/**
 * Compile a node and return a nodeLinkFn based on the
 * node type.
 *
 * @param {Node} node
 * @param {Object} options
 * @return {Function|undefined}
 */

function compileNode (node, options) {
  var type = node.nodeType
  if (type === 1 && node.tagName !== 'SCRIPT') {
    return compileElement(node, options)
  } else if (type === 3 && config.interpolate) {
    return compileTextNode(node, options)
  }
}

/**
 * Compile an element and return a nodeLinkFn.
 *
 * @param {Element} el
 * @param {Object} options
 * @return {Function|null}
 */

function compileElement (el, options) {
  var hasAttributes = el.hasAttributes()
  var tag = el.tagName.toLowerCase()
  if (hasAttributes) {
    // check terminal direcitves
    var terminalLinkFn
    for (var i = 0; i < 3; i++) {
      terminalLinkFn = checkTerminalDirectives(el, options)
      if (terminalLinkFn) {
        terminalLinkFn.terminal = true
        return terminalLinkFn
      }
    }
  }
  // check custom element component
  var component =
    tag.indexOf('-') > 0 &&
    options.components[tag]
  if (component) {
    return makeTeriminalLinkFn(el, 'component', tag, options)
  }
  // check other directives
  var linkFn
  if (hasAttributes) {
    var directives = collectDirectives(el, options)
    linkFn = directives.length
      ? makeDirectivesLinkFn(directives)
      : null
  }
  // if the element is a textarea, we need to interpolate
  // its content on initial render.
  if (el.tagName === 'TEXTAREA') {
    var realLinkFn = linkFn
    linkFn = function (vm, el) {
      el.value = vm.$interpolate(el.value)
      if (realLinkFn) realLinkFn(vm, el)
    }
  }
  return linkFn
}

/**
 * Build a multi-directive link function.
 *
 * @param {Array} directives
 * @return {Function} directivesLinkFn
 */

function makeDirectivesLinkFn (directives) {
  return function directivesLinkFn (vm, el) {
    // reverse apply because it's sorted low to high
    var i = directives.length
    var dir, j
    while (i--) {
      dir = directives[i]
      if (dir._link) {
        // custom link fn
        dir._link(vm, el)
      } else {
        j = dir.descriptors.length
        while (j--) {
          vm._bindDir(dir.name, el,
                      dir.descriptors[j], dir.def)
        }
      }
    }
  }
}

/**
 * Compile a textNode and return a nodeLinkFn.
 *
 * @param {TextNode} node
 * @param {Object} options
 * @return {Function|null} textNodeLinkFn
 */

function compileTextNode (node, options) {
  var tokens = textParser.parse(node.nodeValue)
  if (!tokens) {
    return null
  }
  var frag = document.createDocumentFragment()
  var dirs = options.directives
  var el, token, value
  for (var i = 0, l = tokens.length; i < l; i++) {
    token = tokens[i]
    value = token.value
    if (token.tag) {
      if (token.oneTime) {
        el = document.createTextNode(value)
      } else {
        if (token.html) {
          el = document.createComment('v-html')
          token.type = 'html'
          token.def = dirs.html
          token.descriptor = dirParser.parse(value)[0]
        } else if (token.partial) {
          el = document.createComment('v-partial')
          token.type = 'partial'
          token.def = dirs.partial
          token.descriptor = dirParser.parse(value)[0]
        } else {
          // IE will clean up empty textNodes during
          // frag.cloneNode(true), so we have to give it
          // something here...
          el = document.createTextNode(' ')
          token.type = 'text'
          token.def = dirs.text
          token.descriptor = dirParser.parse(value)[0]
        }
      }
    } else {
      el = document.createTextNode(value)
    }
    frag.appendChild(el)
  }
  return makeTextNodeLinkFn(tokens, frag, options)
}

/**
 * Build a function that processes a textNode.
 *
 * @param {Array<Object>} tokens
 * @param {DocumentFragment} frag
 */

function makeTextNodeLinkFn (tokens, frag) {
  return function textNodeLinkFn (vm, el) {
    var fragClone = frag.cloneNode(true)
    var childNodes = _.toArray(fragClone.childNodes)
    var token, value, node
    for (var i = 0, l = tokens.length; i < l; i++) {
      token = tokens[i]
      value = token.value
      if (token.tag) {
        node = childNodes[i]
        if (token.oneTime) {
          value = vm.$eval(value)
          if (token.html) {
            _.replace(node, templateParser.parse(value, true))
          } else {
            node.nodeValue = value
          }
        } else {
          vm._bindDir(token.type, node,
                      token.descriptor, token.def)
        }
      }
    }
    _.replace(el, fragClone)
  }
}

/**
 * Compile a node list and return a childLinkFn.
 *
 * @param {NodeList} nodeList
 * @param {Object} options
 * @return {Function|undefined}
 */

function compileNodeList (nodeList, options) {
  var linkFns = []
  var nodeLinkFn, childLinkFn, node
  for (var i = 0, l = nodeList.length; i < l; i++) {
    node = nodeList[i]
    nodeLinkFn = compileNode(node, options)
    childLinkFn =
      (!nodeLinkFn || !nodeLinkFn.terminal) &&
      node.hasChildNodes()
        ? compileNodeList(node.childNodes, options)
        : null
    linkFns.push(nodeLinkFn, childLinkFn)
  }
  return linkFns.length
    ? makeChildLinkFn(linkFns)
    : null
}

/**
 * Make a child link function for a node's childNodes.
 *
 * @param {Array<Function>} linkFns
 * @return {Function} childLinkFn
 */

function makeChildLinkFn (linkFns) {
  return function childLinkFn (vm, nodes) {
    // stablize nodes
    nodes = _.toArray(nodes)
    var node, nodeLinkFn, childrenLinkFn
    for (var i = 0, n = 0, l = linkFns.length; i < l; n++) {
      node = nodes[n]
      nodeLinkFn = linkFns[i++]
      childrenLinkFn = linkFns[i++]
      if (nodeLinkFn) {
        nodeLinkFn(vm, node)
      }
      if (childrenLinkFn) {
        childrenLinkFn(vm, node.childNodes)
      }
    }
  }
}

/**
 * Compile param attributes on a root element and return
 * a paramAttributes link function.
 *
 * @param {Element} el
 * @param {Array} attrs
 * @param {Object} options
 * @return {Function} paramsLinkFn
 */

function compileParamAttributes (el, attrs, options) {
  var params = []
  var i = attrs.length
  var name, value, param
  while (i--) {
    name = attrs[i]
    value = el.getAttribute(name)
    if (value !== null) {
      param = {
        name: name,
        value: value
      }
      var tokens = textParser.parse(value)
      if (tokens) {
        el.removeAttribute(name)
        if (tokens.length > 1) {
          _.warn(
            'Invalid param attribute binding: "' +
            name + '="' + value + '"' +
            '\nDon\'t mix binding tags with plain text ' +
            'in param attribute bindings.'
          )
          continue
        } else {
          param.dynamic = true
          param.value = tokens[0].value
        }
      }
      params.push(param)
    }
  }
  return makeParamsLinkFn(params, options)
}

/**
 * Build a function that applies param attributes to a vm.
 *
 * @param {Array} params
 * @param {Object} options
 * @return {Function} paramsLinkFn
 */

function makeParamsLinkFn (params, options) {
  var def = options.directives['with']
  return function paramsLinkFn (vm, el) {
    var i = params.length
    var param
    while (i--) {
      param = params[i]
      if (param.dynamic) {
        // dynamic param attribtues are bound as v-with.
        // we can directly duck the descriptor here beacuse
        // param attributes cannot use expressions or
        // filters.
        vm._bindDir('with', el, {
          arg: param.name,
          expression: param.value
        }, def)
      } else {
        // just set once
        vm.$set(param.name, param.value)
      }
    }
  }
}

/**
 * Check an element for terminal directives in fixed order.
 * If it finds one, return a terminal link function.
 *
 * @param {Element} el
 * @param {Object} options
 * @return {Function} terminalLinkFn
 */

var terminalDirectives = [
  'repeat',
  'component',
  'if'
]

function checkTerminalDirectives (el, options) {
  if (_.attr(el, 'pre') !== null) {
    return noop
  }
  var value, dirName
  /* jshint boss: true */
  for (var i = 0; i < 3; i++) {
    dirName = terminalDirectives[i]
    if (value = _.attr(el, dirName)) {
      return makeTeriminalLinkFn(el, dirName, value, options)
    }
  }
}

/**
 * Build a link function for a terminal directive.
 *
 * @param {Element} el
 * @param {String} dirName
 * @param {String} value
 * @param {Object} options
 * @return {Function} terminalLinkFn
 */

function makeTeriminalLinkFn (el, dirName, value, options) {
  var descriptor = dirParser.parse(value)[0]
  var def = options.directives[dirName]
  return function terminalLinkFn (vm, el) {
    vm._bindDir(dirName, el, descriptor, def)
  }
}

/**
 * Collect the directives on an element.
 *
 * @param {Element} el
 * @param {Object} options
 * @return {Array}
 */

function collectDirectives (el, options) {
  var attrs = _.toArray(el.attributes)
  var i = attrs.length
  var dirs = []
  var attr, attrName, dir, dirName, dirDef
  while (i--) {
    attr = attrs[i]
    attrName = attr.name
    if (attrName.indexOf(config.prefix) === 0) {
      dirName = attrName.slice(config.prefix.length)
      dirDef = options.directives[dirName]
      _.assertAsset(dirDef, 'directive', dirName)
      if (dirDef) {
        if (dirName !== 'cloak') {
          el.removeAttribute(attrName)
        }
        dirs.push({
          name: dirName,
          descriptors: dirParser.parse(attr.value),
          def: dirDef
        })
      }
    } else if (config.interpolate) {
      dir = collectAttrDirective(el, attrName, attr.value,
                                 options)
      if (dir) {
        dirs.push(dir)
      }
    }
  }
  // sort by priority, LOW to HIGH
  dirs.sort(directiveComparator)
  return dirs
}

/**
 * Check an attribute for potential dynamic bindings,
 * and return a directive object.
 *
 * @param {Element} el
 * @param {String} name
 * @param {String} value
 * @param {Object} options
 * @return {Object}
 */

function collectAttrDirective (el, name, value, options) {
  var tokens = textParser.parse(value)
  if (tokens) {
    var def = options.directives.attr
    var i = tokens.length
    var allOneTime = true
    while (i--) {
      var token = tokens[i]
      if (token.tag && !token.oneTime) {
        allOneTime = false
      }
    }
    return {
      def: def,
      _link: allOneTime
        ? function (vm, el) {
            el.setAttribute(name, vm.$interpolate(value))
          }
        : function (vm, el) {
            var value = textParser.tokensToExp(tokens, vm)
            var desc = dirParser.parse(name + ':' + value)[0]
            vm._bindDir('attr', el, desc, def)
          }
    }
  }
}

/**
 * Directive priority sort comparator
 *
 * @param {Object} a
 * @param {Object} b
 */

function directiveComparator (a, b) {
  a = a.def.priority || 0
  b = b.def.priority || 0
  return a > b ? 1 : -1
}<|endoftext|>


<commit_before>'use strict';

var solc = require('solc/wrapper');

var webworkify = require('webworkify');
var utils = require('./utils');

var EventManager = require('../lib/eventManager');

/*
  trigger compilationFinished, compilerLoaded, compilationStarted
*/
function Compiler (editor, handleGithubCall) {
  var self = this;
  this.event = new EventManager();

  var compileJSON;
  var compilerAcceptsMultipleFiles;

  var cachedRemoteFiles = {};
  var worker = null;

  var optimize = false;

  this.setOptimize = function (_optimize) {
    optimize = _optimize;
  };

  var compile = function (missingInputs) {
    editor.clearAnnotations();
    self.event.trigger('compilationStarted', []);
    var input = editor.getValue();
    editor.setCacheFileContent(input);

    var files = {};
    files[utils.fileNameFromKey(editor.getCacheFile())] = input;
    gatherImports(files, missingInputs, function (input, error) {
      if (input === null) {
        self.lastCompilationResult = null;
        self.event.trigger('compilationFinished', [false, { 'error': error }, files]);
      } else {
        compileJSON(input, optimize ? 1 : 0);
      }
    });
  };
  this.compile = compile;

  function setCompileJSON (_compileJSON) {
    compileJSON = _compileJSON;
  }
  this.setCompileJSON = setCompileJSON; // this is exposed for testing

  function onCompilerLoaded (version) {
    self.event.trigger('compilerLoaded', [version]);
  }

  function onInternalCompilerLoaded () {
    if (worker === null) {
      var compiler = solc(window.Module);

      compilerAcceptsMultipleFiles = compiler.supportsMulti;

      compileJSON = function (source, optimize, cb) {
        var missingInputs = [];
        var missingInputsCallback = function (path) {
          missingInputs.push(path);
          return { error: 'Deferred import' };
        };

        var result;
        try {
          result = compiler.compile(source, optimize, missingInputsCallback);
        } catch (exception) {
          result = { error: 'Uncaught JavaScript exception:\n' + exception };
        }

        compilationFinished(result, missingInputs, source);
      };

      onCompilerLoaded(compiler.version());
    }
  }

  this.lastCompilationResult = {
    data: null,
    source: null
  };
  function compilationFinished (data, missingInputs, source) {
    var noFatalErrors = true; // ie warnings are ok

    function isValidError (error) {
      // The deferred import is not a real error
      // FIXME: maybe have a better check?
      if (/Deferred import/.exec(error)) {
        return false;
      }

      return utils.errortype(error) !== 'warning';
    }

    if (data['error'] !== undefined) {
      // Ignore warnings (and the 'Deferred import' error as those are generated by us as a workaround
      if (isValidError(data['error'])) {
        noFatalErrors = false;
      }
    }
    if (data['errors'] !== undefined) {
      data['errors'].forEach(function (err) {
        // Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround
        if (isValidError(err)) {
          noFatalErrors = false;
        }
      });
    }

    if (!noFatalErrors) {
      // There are fatal errors - abort here
      self.lastCompilationResult = null;
      self.event.trigger('compilationFinished', [false, data, source]);
    } else if (missingInputs !== undefined && missingInputs.length > 0) {
      compile(missingInputs);
    } else {
      self.lastCompilationResult = {
        data: data,
        source: source
      };
      self.event.trigger('compilationFinished', [true, data, source]);
    }
  }

  this.loadVersion = function (usingWorker, url) {
    console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker'));
    self.event.trigger('loadingCompiler', [url, usingWorker]);

    if (usingWorker) {
      loadWorker(url);
    } else {
      loadInternal(url);
    }
  };

  function loadInternal (url) {
    delete window.Module;
    // NOTE: workaround some browsers?
    window.Module = undefined;

    // Set a safe fallback until the new one is loaded
    setCompileJSON(function (source, optimize) {
      compilationFinished({error: 'Compiler not yet loaded.'});
    });

    var newScript = document.createElement('script');
    newScript.type = 'text/javascript';
    newScript.src = url;
    document.getElementsByTagName('head')[0].appendChild(newScript);
    var check = window.setInterval(function () {
      if (!window.Module) {
        return;
      }
      window.clearInterval(check);
      onInternalCompilerLoaded();
    }, 200);
  }

  function loadWorker (url) {
    if (worker !== null) {
      worker.terminate();
    }
    worker = webworkify(require('./compiler-worker.js'));
    var jobs = [];
    worker.addEventListener('message', function (msg) {
      var data = msg.data;
      switch (data.cmd) {
        case 'versionLoaded':
          compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles;
          onCompilerLoaded(data.data);
          break;
        case 'compiled':
          var result;
          try {
            result = JSON.parse(data.data);
          } catch (exception) {
            result = { 'error': 'Invalid JSON output from the compiler: ' + exception };
          }
          var sources = {};
          if (data.job in jobs !== undefined) {
            sources = jobs[data.job].sources;
            delete jobs[data.job];
          }
          compilationFinished(result, data.missingInputs, sources);
          break;
      }
    });
    worker.onerror = function (msg) {
      compilationFinished({ error: 'Worker error: ' + msg.data });
    };
    worker.addEventListener('error', function (msg) {
      compilationFinished({ error: 'Worker error: ' + msg.data });
    });
    compileJSON = function (source, optimize) {
      jobs.push({sources: source});
      worker.postMessage({cmd: 'compile', job: jobs.length - 1, source: JSON.stringify(source), optimize: optimize});
    };
    worker.postMessage({cmd: 'loadVersion', data: url});
  }

  function gatherImports (files, importHints, cb) {
    importHints = importHints || [];
    if (!compilerAcceptsMultipleFiles) {
      cb(files[editor.getCacheFile()]);
      return;
    }
    var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g;
    var reloop = false;
    var githubMatch;
    do {
      reloop = false;
      for (var fileName in files) {
        var match;
        while ((match = importRegex.exec(files[fileName]))) {
          var importFilePath = match[1];
          if (importFilePath.startsWith('./')) {
            importFilePath = importFilePath.slice(2);
          }
          importHints.push(importFilePath);
        }
      }
      while (importHints.length > 0) {
        var m = importHints.pop();
        if (m in files) {
          continue;
        }
        if (editor.hasFile(m)) {
          files[m] = editor.getFile(m);
          reloop = true;
        } else if (m in cachedRemoteFiles) {
          files[m] = cachedRemoteFiles[m];
          reloop = true;
        } else if ((githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m))) {
          handleGithubCall(githubMatch[3], githubMatch[4], function (err, content) {
            if (err) {
              cb(null, 'Unable to import "' + m + '": ' + err);
              return;
            }

            cachedRemoteFiles[m] = content;
            files[m] = content;

            gatherImports(files, importHints, cb);
          });
          return;
        } else if (/^[^:]*:\/\//.exec(m)) {
          cb(null, 'Unable to import "' + m + '": Unsupported URL');
          return;
        } else {
          cb(null, 'Unable to import "' + m + '": File not found');
          return;
        }
      }
    } while (reloop);
    cb({ 'sources': files });
  }
}

module.exports = Compiler;
<commit_msg>Use absolute url for worker.<commit_after>'use strict';

var solc = require('solc/wrapper');

var webworkify = require('webworkify');
var utils = require('./utils');

var EventManager = require('../lib/eventManager');

/*
  trigger compilationFinished, compilerLoaded, compilationStarted
*/
function Compiler (editor, handleGithubCall) {
  var self = this;
  this.event = new EventManager();

  var compileJSON;
  var compilerAcceptsMultipleFiles;

  var cachedRemoteFiles = {};
  var worker = null;

  var optimize = false;

  this.setOptimize = function (_optimize) {
    optimize = _optimize;
  };

  var compile = function (missingInputs) {
    editor.clearAnnotations();
    self.event.trigger('compilationStarted', []);
    var input = editor.getValue();
    editor.setCacheFileContent(input);

    var files = {};
    files[utils.fileNameFromKey(editor.getCacheFile())] = input;
    gatherImports(files, missingInputs, function (input, error) {
      if (input === null) {
        self.lastCompilationResult = null;
        self.event.trigger('compilationFinished', [false, { 'error': error }, files]);
      } else {
        compileJSON(input, optimize ? 1 : 0);
      }
    });
  };
  this.compile = compile;

  function setCompileJSON (_compileJSON) {
    compileJSON = _compileJSON;
  }
  this.setCompileJSON = setCompileJSON; // this is exposed for testing

  function onCompilerLoaded (version) {
    self.event.trigger('compilerLoaded', [version]);
  }

  function onInternalCompilerLoaded () {
    if (worker === null) {
      var compiler = solc(window.Module);

      compilerAcceptsMultipleFiles = compiler.supportsMulti;

      compileJSON = function (source, optimize, cb) {
        var missingInputs = [];
        var missingInputsCallback = function (path) {
          missingInputs.push(path);
          return { error: 'Deferred import' };
        };

        var result;
        try {
          result = compiler.compile(source, optimize, missingInputsCallback);
        } catch (exception) {
          result = { error: 'Uncaught JavaScript exception:\n' + exception };
        }

        compilationFinished(result, missingInputs, source);
      };

      onCompilerLoaded(compiler.version());
    }
  }

  this.lastCompilationResult = {
    data: null,
    source: null
  };
  function compilationFinished (data, missingInputs, source) {
    var noFatalErrors = true; // ie warnings are ok

    function isValidError (error) {
      // The deferred import is not a real error
      // FIXME: maybe have a better check?
      if (/Deferred import/.exec(error)) {
        return false;
      }

      return utils.errortype(error) !== 'warning';
    }

    if (data['error'] !== undefined) {
      // Ignore warnings (and the 'Deferred import' error as those are generated by us as a workaround
      if (isValidError(data['error'])) {
        noFatalErrors = false;
      }
    }
    if (data['errors'] !== undefined) {
      data['errors'].forEach(function (err) {
        // Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround
        if (isValidError(err)) {
          noFatalErrors = false;
        }
      });
    }

    if (!noFatalErrors) {
      // There are fatal errors - abort here
      self.lastCompilationResult = null;
      self.event.trigger('compilationFinished', [false, data, source]);
    } else if (missingInputs !== undefined && missingInputs.length > 0) {
      compile(missingInputs);
    } else {
      self.lastCompilationResult = {
        data: data,
        source: source
      };
      self.event.trigger('compilationFinished', [true, data, source]);
    }
  }

  this.loadVersion = function (usingWorker, url) {
    console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker'));
    self.event.trigger('loadingCompiler', [url, usingWorker]);

    if (usingWorker) {
      loadWorker(url);
    } else {
      loadInternal(url);
    }
  };

  function loadInternal (url) {
    delete window.Module;
    // NOTE: workaround some browsers?
    window.Module = undefined;

    // Set a safe fallback until the new one is loaded
    setCompileJSON(function (source, optimize) {
      compilationFinished({error: 'Compiler not yet loaded.'});
    });

    var newScript = document.createElement('script');
    newScript.type = 'text/javascript';
    newScript.src = url;
    document.getElementsByTagName('head')[0].appendChild(newScript);
    var check = window.setInterval(function () {
      if (!window.Module) {
        return;
      }
      window.clearInterval(check);
      onInternalCompilerLoaded();
    }, 200);
  }

  function loadWorker (url) {
    if (worker !== null) {
      worker.terminate();
    }
    worker = webworkify(require('./compiler-worker.js'));
    var jobs = [];
    worker.addEventListener('message', function (msg) {
      var data = msg.data;
      switch (data.cmd) {
        case 'versionLoaded':
          compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles;
          onCompilerLoaded(data.data);
          break;
        case 'compiled':
          var result;
          try {
            result = JSON.parse(data.data);
          } catch (exception) {
            result = { 'error': 'Invalid JSON output from the compiler: ' + exception };
          }
          var sources = {};
          if (data.job in jobs !== undefined) {
            sources = jobs[data.job].sources;
            delete jobs[data.job];
          }
          compilationFinished(result, data.missingInputs, sources);
          break;
      }
    });
    worker.onerror = function (msg) {
      compilationFinished({ error: 'Worker error: ' + msg.data });
    };
    worker.addEventListener('error', function (msg) {
      compilationFinished({ error: 'Worker error: ' + msg.data });
    });
    compileJSON = function (source, optimize) {
      jobs.push({sources: source});
      worker.postMessage({cmd: 'compile', job: jobs.length - 1, source: JSON.stringify(source), optimize: optimize});
    };
    if (!url.startsWith('http') && !url.startsWith('file')) {
      var loc = window.document.location;
      var locStr = loc.protocol + '//' + loc.host + '/' + loc.pathname;
      if (!locStr.endsWith('/'))
        locStr += '/';
      url = locStr + url;
    }
    worker.postMessage({cmd: 'loadVersion', data: url});
  }

  function gatherImports (files, importHints, cb) {
    importHints = importHints || [];
    if (!compilerAcceptsMultipleFiles) {
      cb(files[editor.getCacheFile()]);
      return;
    }
    var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g;
    var reloop = false;
    var githubMatch;
    do {
      reloop = false;
      for (var fileName in files) {
        var match;
        while ((match = importRegex.exec(files[fileName]))) {
          var importFilePath = match[1];
          if (importFilePath.startsWith('./')) {
            importFilePath = importFilePath.slice(2);
          }
          importHints.push(importFilePath);
        }
      }
      while (importHints.length > 0) {
        var m = importHints.pop();
        if (m in files) {
          continue;
        }
        if (editor.hasFile(m)) {
          files[m] = editor.getFile(m);
          reloop = true;
        } else if (m in cachedRemoteFiles) {
          files[m] = cachedRemoteFiles[m];
          reloop = true;
        } else if ((githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m))) {
          handleGithubCall(githubMatch[3], githubMatch[4], function (err, content) {
            if (err) {
              cb(null, 'Unable to import "' + m + '": ' + err);
              return;
            }

            cachedRemoteFiles[m] = content;
            files[m] = content;

            gatherImports(files, importHints, cb);
          });
          return;
        } else if (/^[^:]*:\/\//.exec(m)) {
          cb(null, 'Unable to import "' + m + '": Unsupported URL');
          return;
        } else {
          cb(null, 'Unable to import "' + m + '": File not found');
          return;
        }
      }
    } while (reloop);
    cb({ 'sources': files });
  }
}

module.exports = Compiler;
<|endoftext|>


<commit_before>/// <reference path="../../../typings/phaser/phaser.d.ts"/>

/**
 * Each section of the site has its own module. It probably also has
 * submodules, though this boilerplate is too simple to demonstrate it. Within
 * `src/app/home`, however, could exist several additional folders representing
 * additional modules that would then be listed as dependencies of this one.
 * For example, a `note` section could have the submodules `note.create`,
 * `note.delete`, `note.edit`, etc.
 *
 * Regardless, so long as dependencies are managed correctly, the build process
 * will automatically take take of the rest.
 *
 * The dependencies block here is also where component dependencies should be
 * specified, as shown below.
 */

angular.module( 'ngBoilerplate.home', [
  'ui.router',
  'plusOne'
])

/**
 * Each section or module of the site can also have its own routes. AngularJS
 * will handle ensuring they are all available at run-time, but splitting it
 * this way makes each module more "self-contained".
 */
.config(function config( $stateProvider ) {
  $stateProvider.state( 'home', {
    url: '/home',
    views: {
      "main": {
        controller: 'HomeCtrl',
        templateUrl: 'home/home.tpl.html'
      }
    },
    data:{ pageTitle: 'Home' }
  });
})

/**
 * And of course we define a controller for our route.
 */
.controller( 'HomeCtrl', function HomeController( $scope ) {

});

var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render });

function preload() {
    game.load.image('circle', 'assets/circle.png');
    game.time.advancedTiming = true;
}

var sprite;
var emitter;

function create() {
    game.stage.backgroundColor = '#1F1F1F';

    emitter = game.add.emitter(game.world.centerX, game.world.centerY, 250);
    emitter.makeParticles('circle', 0, 2000, true, true);

    emitter.minParticleScale = 0.01;
    emitter.maxParticleScale = 0.02;
    emitter.minParticleSpeed.setTo(-200, -300);
    emitter.maxParticleSpeed.setTo(200, -400);
    emitter.gravity = 500;
    emitter.bounce.setTo(0.5, 0.5);
    emitter.angularDrag = 30;

    emitter.start(false, 15000, 0);

    updateGravity();

    game.time.events.loop(Phaser.Timer.SECOND * 10, updateGravity, this);

}
var gravity;
var frequency;

function updateGravity() {
  gravity = game.rnd.pick([0, 50, 100, 200, 500, 1000]);
  frequency = game.rnd.pick([0, 10]);

  emitter.gravity = gravity;
  emitter.frequency = frequency;
}

function update() {

     game.physics.arcade.collide(emitter);

}

function render() {
  game.debug.text("FPS: " + game.time.fps || '--', 2, 10, "#00ff00");
  game.debug.text("Gravity: " + gravity, 2, 25, "#00ff00");
  game.debug.text("Frequency: " + frequency, 2, 40, "#00ff00");
  game.debug.text("Time: " + game.time.events.duration.toFixed(0), 2, 55, "#00ff00");
  game.debug.text("Living: " + emitter.total, 2, 70, "#00ff00");
    //game.debug.spriteInfo(sprite, 32, 32);

}
<commit_msg>Adjust max size.<commit_after>/// <reference path="../../../typings/phaser/phaser.d.ts"/>

/**
 * Each section of the site has its own module. It probably also has
 * submodules, though this boilerplate is too simple to demonstrate it. Within
 * `src/app/home`, however, could exist several additional folders representing
 * additional modules that would then be listed as dependencies of this one.
 * For example, a `note` section could have the submodules `note.create`,
 * `note.delete`, `note.edit`, etc.
 *
 * Regardless, so long as dependencies are managed correctly, the build process
 * will automatically take take of the rest.
 *
 * The dependencies block here is also where component dependencies should be
 * specified, as shown below.
 */

angular.module( 'ngBoilerplate.home', [
  'ui.router',
  'plusOne'
])

/**
 * Each section or module of the site can also have its own routes. AngularJS
 * will handle ensuring they are all available at run-time, but splitting it
 * this way makes each module more "self-contained".
 */
.config(function config( $stateProvider ) {
  $stateProvider.state( 'home', {
    url: '/home',
    views: {
      "main": {
        controller: 'HomeCtrl',
        templateUrl: 'home/home.tpl.html'
      }
    },
    data:{ pageTitle: 'Home' }
  });
})

/**
 * And of course we define a controller for our route.
 */
.controller( 'HomeCtrl', function HomeController( $scope ) {

});

var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render });

function preload() {
    game.load.image('circle', 'assets/circle.png');
    game.time.advancedTiming = true;
}

var sprite;
var emitter;

function create() {
    game.stage.backgroundColor = '#1F1F1F';

    emitter = game.add.emitter(game.world.centerX, game.world.centerY, 250);
    emitter.makeParticles('circle', 0, 2000, true, true);

    emitter.minParticleScale = 0.01;
    emitter.maxParticleScale = 0.03;
    emitter.minParticleSpeed.setTo(-200, -300);
    emitter.maxParticleSpeed.setTo(200, -400);
    emitter.gravity = 500;
    emitter.bounce.setTo(0.5, 0.5);
    emitter.angularDrag = 30;

    emitter.start(false, 15000, 0);

    updateGravity();

    game.time.events.loop(Phaser.Timer.SECOND * 10, updateGravity, this);

}
var gravity;
var frequency;

function updateGravity() {
  gravity = game.rnd.pick([0, 50, 100, 200, 500, 1000]);
  frequency = game.rnd.pick([0, 10]);

  emitter.gravity = gravity;
  emitter.frequency = frequency;
}

function update() {

     game.physics.arcade.collide(emitter);

}

function render() {
  game.debug.text("FPS: " + game.time.fps || '--', 2, 10, "#00ff00");
  game.debug.text("Gravity: " + gravity, 2, 25, "#00ff00");
  game.debug.text("Frequency: " + frequency, 2, 40, "#00ff00");
  game.debug.text("Time: " + game.time.events.duration.toFixed(0), 2, 55, "#00ff00");
  game.debug.text("Living: " + emitter.total, 2, 70, "#00ff00");
    //game.debug.spriteInfo(sprite, 32, 32);

}
<|endoftext|>


<commit_before>var should = require('should');
var assert = require('assert');
var LWTE = require('../lwte.min.js');

lwte = new LWTE();

describe("compile", function(){
    describe("success", function(){
    });
    describe("failure", function(){
        describe("not closed", function(){
            it("if is not closed", function(){lwte.addTemplate("a", "{if a}a{else}").should.not.be.exactly(false)});
            it("each is not closed", function(){lwte.addTemplate("a", "{each a}a").should.not.be.exactly(false)});
            it("switch is not closed", function(){lwte.addTemplate("a", "{switch a}a").should.not.be.exactly(false)});
        });
        it("elif after else", function(){lwte.addTemplate("a", "{if a}a{else}b{elif}c{/if}").should.not.be.exactly(false)});
    });
});
describe("use", function(){
    describe("success", function(){
        it("variable", function(){
            lwte.addTemplate("a", "{a}");
            lwte.useTemplate("a", {a: "<>&\"'"}).should.be.exactly("<>&"'");
        });
        it("html", function(){
            lwte.addTemplate("a", "{html a}");
            lwte.useTemplate("a", {a: "<>&\"'"}).should.be.exactly("<>&\"'");
        });
        it("if", function(){
            lwte.addTemplate("a", "{if a}a{elif b}b{elif c}c{elif d}d{elif e}e{elif f}f{else}else{/if}{if f e}fe{elif f g}fg{/if}");
            lwte.useTemplate("a", {a: false, b: [], c: {}, d: null, e: undefined, f: 0, g: true}).should.be.exactly("ffg");
        });
        it("switch", function(){
            lwte.addTemplate("a", "{switch a}{case aaa} aaa {case aa} aa {case aaa aa} aaaaa {default} default{/switch}");
            lwte.useTemplate("a", {a: "aaa"}).should.be.exactly(" aaa  aaaaa ");
        });
        it("each", function(){
            lwte.addTemplate("a", "{each a}aa{item}{if a}aa{/if}{/each}");
            lwte.useTemplate("a", {a: [{a: true, item: "b"}, {item: "c"}, {item: "d"}, {item: "e"}]}).should.be.exactly("aabaaaacaadaae");
        });
        it("use", function(){
            lwte.addTemplate("a", "bbbb{item}");
            lwte.addTemplate("b", "aa{use a a}aa");
            lwte.useTemplate("b", {a: {item: "ITEM"}}).should.be.exactly("aabbbbITEMaa");
        });
    });
});
<commit_msg>Add a test case which tests nested each<commit_after>var should = require('should');
var assert = require('assert');
var LWTE = require('../lwte.min.js');

lwte = new LWTE();

describe("compile", function(){
    describe("success", function(){
    });
    describe("failure", function(){
        describe("not closed", function(){
            it("if is not closed", function(){lwte.addTemplate("a", "{if a}a{else}").should.not.be.exactly(false)});
            it("each is not closed", function(){lwte.addTemplate("a", "{each a}a").should.not.be.exactly(false)});
            it("switch is not closed", function(){lwte.addTemplate("a", "{switch a}a").should.not.be.exactly(false)});
        });
        it("elif after else", function(){lwte.addTemplate("a", "{if a}a{else}b{elif}c{/if}").should.not.be.exactly(false)});
    });
});
describe("use", function(){
    describe("success", function(){
        it("variable", function(){
            lwte.addTemplate("a", "{a}");
            lwte.useTemplate("a", {a: "<>&\"'"}).should.be.exactly("<>&"'");
        });
        it("html", function(){
            lwte.addTemplate("a", "{html a}");
            lwte.useTemplate("a", {a: "<>&\"'"}).should.be.exactly("<>&\"'");
        });
        it("if", function(){
            lwte.addTemplate("a", "{if a}a{elif b}b{elif c}c{elif d}d{elif e}e{elif f}f{else}else{/if}{if f e}fe{elif f g}fg{/if}");
            lwte.useTemplate("a", {a: false, b: [], c: {}, d: null, e: undefined, f: 0, g: true}).should.be.exactly("ffg");
        });
        it("switch", function(){
            lwte.addTemplate("a", "{switch a}{case aaa} aaa {case aa} aa {case aaa aa} aaaaa {default} default{/switch}");
            lwte.useTemplate("a", {a: "aaa"}).should.be.exactly(" aaa  aaaaa ");
        });
        it("each", function(){
            lwte.addTemplate("a", "{each a}aa{item}{if a}aa{/if}{/each}");
            lwte.useTemplate("a", {a: [{a: true, item: "b"}, {item: "c"}, {item: "d"}, {item: "e"}]}).should.be.exactly("aabaaaacaadaae");
            lwte.addTemplate("a", "{each a}[{each b}({item}){/each}]{/each}");
            lwte.useTemplate("a", {a: [{b: [{item: "e"}, {item: "f"}]}, {b: [{item: "g"}, {item: "h"}]}]}).should.be.exactly("[(e)(f)][(g)(h)]");
        });
        it("use", function(){
            lwte.addTemplate("a", "bbbb{item}");
            lwte.addTemplate("b", "aa{use a a}aa");
            lwte.useTemplate("b", {a: {item: "ITEM"}}).should.be.exactly("aabbbbITEMaa");
        });
    });
});
<|endoftext|>


<commit_before>import { error } from "../util/util";

const WHITESPACE_RE = /\s/;

const pushChild = (child, stack) => {
  stack[stack.length - 1].children.push(child);
};

const parseOpeningTag = (index, input, length, stack) => {
  let type = "";

  for (; index < length; index++) {
    const char = input[index];

    if (char === ">") {
      const element = {
        type: type,
        children: []
      };

      pushChild(element, stack);
      stack.push(element);

      index += 1;
      break;
    } else if (char === "/" && input[index + 1] === ">") {
      pushChild({
        type: type,
        children: []
      }, stack);

      index += 2;
      break;
    } else {
      type += char;
    }
  }

  return index;
};

const parseClosingTag = (index, input, length, stack) => {
  let type = "";

  for(; index < length; index++) {
    const char = input[index];

    if (char === ">") {
      index += 1;
      break;
    } else {
      type += char;
    }
  }

  const lastElement = stack.pop();
  if (type !== lastElement.type && process.env.MOON_ENV === "development") {
    error(`Unclosed tag "${lastElement.type}", expected "${type}"`);
  }

  return index;
};

const parseText = (index, input, length, stack) => {
  let content = "";

  for (; index < length; index++) {
    const char = input[index];

    if (char === "<") {
      break;
    } else {
      content += char;
    }
  }

  pushChild({
    type: "m-text",
    content: content
  }, stack);

  return index;
};

const parse = (input) => {
  const length = input.length;

  const root = {
    type: "m-fragment",
    children: []
  };

  let stack = [root];

  for (let i = 0; i < length;) {
    const char = input[i];

    if (char === "<") {
      if (input[i + 1] === "!" && input[i + 2] === "-" && input[i + 3] === "-") {
        i = parseComment(i + 4, input, length, stack);
      } else if (input[i + 1] === "/") {
        i = parseClosingTag(i + 2, input, length, stack);
      } else {
        i = parseOpeningTag(i + 1, input, length, stack);
      }
    } else {
      i = parseText(i, input, length, stack);
    }
  }

  return root;
};
<commit_msg>export parser<commit_after>import { error } from "../util/util";

const WHITESPACE_RE = /\s/;

const pushChild = (child, stack) => {
  stack[stack.length - 1].children.push(child);
};

const parseOpeningTag = (index, input, length, stack) => {
  let type = "";

  for (; index < length; index++) {
    const char = input[index];

    if (char === ">") {
      const element = {
        type: type,
        children: []
      };

      pushChild(element, stack);
      stack.push(element);

      index += 1;
      break;
    } else if (char === "/" && input[index + 1] === ">") {
      pushChild({
        type: type,
        children: []
      }, stack);

      index += 2;
      break;
    } else {
      type += char;
    }
  }

  return index;
};

const parseClosingTag = (index, input, length, stack) => {
  let type = "";

  for(; index < length; index++) {
    const char = input[index];

    if (char === ">") {
      index += 1;
      break;
    } else {
      type += char;
    }
  }

  const lastElement = stack.pop();
  if (type !== lastElement.type && process.env.MOON_ENV === "development") {
    error(`Unclosed tag "${lastElement.type}", expected "${type}"`);
  }

  return index;
};

const parseText = (index, input, length, stack) => {
  let content = "";

  for (; index < length; index++) {
    const char = input[index];

    if (char === "<") {
      break;
    } else {
      content += char;
    }
  }

  pushChild({
    type: "m-text",
    content: content
  }, stack);

  return index;
};

export const parse = (input) => {
  const length = input.length;

  const root = {
    type: "m-fragment",
    children: []
  };

  let stack = [root];

  for (let i = 0; i < length;) {
    const char = input[i];

    if (char === "<") {
      if (input[i + 1] === "!" && input[i + 2] === "-" && input[i + 3] === "-") {
        i = parseComment(i + 4, input, length, stack);
      } else if (input[i + 1] === "/") {
        i = parseClosingTag(i + 2, input, length, stack);
      } else {
        i = parseOpeningTag(i + 1, input, length, stack);
      }
    } else {
      i = parseText(i, input, length, stack);
    }
  }

  return root;
};
<|endoftext|>


<commit_before>require({
    packages: [
        'agrc',
        'app',
        'dgrid',
        'dgrid1',
        'dgauges',
        'dijit',
        'dojo',
        'dojox',
        'dstore',
        'esri',
        'ijit',
        'layer-selector',
        'moment',
        'put-selector',
        'xstyle',
        {
            name: 'bootstrap',
            location: './bootstrap',
            main: 'dist/js/bootstrap'
        }, {
            name: 'jquery',
            location: './jquery/dist',
            main: 'jquery'
        }, {
            name: 'ladda',
            location: './ladda-bootstrap',
            main: 'dist/ladda'
        }, {
            name: 'mustache',
            location: './mustache',
            main: 'mustache'
        }, {
            name: 'proj4',
            location: './proj4/dist',
            main: 'proj4'
        }, {
            name: 'spin',
            location: './spinjs',
            main: 'spin'
        }
    ]
});
<commit_msg>remove dgauges<commit_after>require({
    packages: [
        'agrc',
        'app',
        'dgrid',
        'dgrid1',
        'dijit',
        'dojo',
        'dojox',
        'dstore',
        'esri',
        'ijit',
        'layer-selector',
        'moment',
        'put-selector',
        'xstyle',
        {
            name: 'bootstrap',
            location: './bootstrap',
            main: 'dist/js/bootstrap'
        }, {
            name: 'jquery',
            location: './jquery/dist',
            main: 'jquery'
        }, {
            name: 'ladda',
            location: './ladda-bootstrap',
            main: 'dist/ladda'
        }, {
            name: 'mustache',
            location: './mustache',
            main: 'mustache'
        }, {
            name: 'proj4',
            location: './proj4/dist',
            main: 'proj4'
        }, {
            name: 'spin',
            location: './spinjs',
            main: 'spin'
        }
    ]
});
<|endoftext|>


<commit_before>angular.module('shopper.home', [
    'ui.router'
])
.config(function config($stateProvider ) {
    $stateProvider.state('home', {
        url: '/home',
        views: {
            'main': {
                controller: 'HomeCtrl',
                templateUrl: 'home/home.tpl.html'
            }
        }
    });
})
.controller('HomeCtrl', function($scope, $rootScope, $timeout, $translate, $window, HomeService) {
    $scope.$on('bodyClicked', function(scope, event) {
        var productSearchQueryKey = 'productSearchQuery';
        if ([
                productSearchQueryKey,
                'add-custom-product'
            ].indexOf(event.target.id !== -1))
            return;
        $scope[productSearchQueryKey] = '';
    });

    $scope.$on('updateAllProducts', function(scope, data) {
        $scope.allProducts = data;
    });

    $scope.$on('updateLists', function(scope, data) {
        $scope.lists = data;
    });

    $scope.$on('goToFirstList', function(scope) {
        if ($scope.currentListIndex > 0) {
            $scope.currentListIndex--;
        }
    });

    $scope.$on('hideListDetail', function(scope) {
        $scope.showMe = true;
    });

    $scope.$watch('currentListIndex', function(newValue) {
        if ($scope.lists[newValue]) {
            localStorage.lastListId = $scope.lists[newValue].id;
        }
    });

    $scope.refresh = function(initialLoad) {
        var allProductsSorted = [];
        var allProductsClean = JSON.parse(angular.toJson($scope.allProducts));
        for (var i = 0; i < allProductsClean.length; i++) {
            var arrayOfSortedKeyValues = [];
            var keys = Object.keys(allProductsClean[i]).sort();
            for (var j = 0; j < keys.length; j++) {
                arrayOfSortedKeyValues.push(allProductsClean[i][keys[j]]);
            }
            allProductsSorted.push(arrayOfSortedKeyValues);
        }
        var allProductsSortedJsonString = JSON.stringify(allProductsSorted);
        HomeService.getAllProducts(SparkMD5.hash(allProductsSortedJsonString));

        var listsClean = JSON.parse(angular.toJson($scope.lists));
        var listsStringified = JSON.stringify(listsClean);
        HomeService.getListsWithProductsOfUser(SparkMD5.hash(listsStringified)).then(function() {
            if (initialLoad) {
                $timeout(function() {
                    if (localStorage.lastListId) {
                        for (var i = 0; i < $scope.lists.length; i++) {
                            if ($scope.lists[i].id.toString() === localStorage.lastListId) {
                                $scope.currentListIndex = i;
                                break;
                            }
                        }
                    }
                });
            }
        });

        $timeout(function() {
            $scope.refresh();
        }, 1500);
    };

    $scope.addCustomProductToList = function(name) {
        if (name.trim() === '')
            return;

        HomeService.addCustomProductToList($scope.capitalizeString(name), $scope.lists[$scope.currentListIndex]).success(function(data) {
            $scope.refresh();
        });

        $scope.productSearchQuery = '';
    };

    $scope.isProductInList = function(product) {
        if (getCurrentList() === undefined) {
            return false;
        }

        for (var i = 0; i < getCurrentList().products.length; i++) {
            if (product.id === getCurrentList().products[i].id && getCurrentList().products[i].active) {
                return true;
            }
        }
        return false;
    };

    $scope.addProductToList = function(product) {
        if ($scope.isProductInList(product))
            return;

        HomeService.addProductToList(product, getCurrentList()).then(function() {
            $scope.refresh();
        });

        $scope.productSearchQuery = '';
    };

    $scope.changeActiveStateOfProductFromList = function(product, active) {
        HomeService.changeActiveStateOfProductFromList(product, getCurrentList(), active);
        getCurrentList().products[getCurrentList().products.indexOf(product)].disabled = true;
    };

    $scope.addList = function() {
        $translate('WHAT_SHOULD_WE_CALL_IT').then(function(translation) {
            var name = prompt(translation);
            if (! name)
                return;
            HomeService.addList(name).success(function(data) {
                HomeService.getListsWithProductsOfUser().success(function() {
                    $timeout(function() {
                        $scope.currentListIndex = $scope.lists.length - 1;
                    });
                });
            });
        });

    };

    $scope.editList = function(index) {
        $scope.showMe = false;
        $rootScope.$broadcast('showListDetail', $scope.lists[index]);
    };

    $scope.editComment = function (product, list) {
        $translate('CHANGE_COMMENT').then(function(translation) {
            $timeout(function() {
                var oldComment = (typeof product.comment === 'string') ? product.comment : '';
                var newComment = $window.prompt(translation, oldComment);
                if (newComment === null || newComment === oldComment)
                    return;
                product.disabled = true;
                HomeService.changeComment(product, list, newComment).then(function () {
                    $scope.refresh();
                });
            }, 0);
        });
    };

    $scope.moveListOnePosition = function(direction) {
        var currentListIndex = $scope.currentListIndex;
        var otherListIndex = $scope.currentListIndex + direction;

        if (otherListIndex < 0 || otherListIndex > $scope.lists.length - 1) {
            return;
        }

        $timeout(function() {
            $scope.currentListIndex = otherListIndex;
        });
        var otherList = $scope.lists[otherListIndex];
        $scope.lists[otherListIndex] = $scope.lists[currentListIndex];
        $scope.lists[currentListIndex] = otherList;

        HomeService.moveListOnePosition($scope.lists[otherListIndex].id, direction);
    };

    $scope.capitalizeString = function(str) {
        if (typeof str === 'string') {
            return str.charAt(0).toUpperCase() + str.slice(1);
        }
        return '';
    };

    $scope.isAProductName = function (searchQuery) {
        if (searchQuery === undefined)
            return false;
        searchQuery = searchQuery.toLowerCase();
        for (var i = 0; i < $scope.allProducts.length; i++) {
            if (searchQuery === $scope.allProducts[i].name.toLowerCase())
                return true;
        }
        return false;
    }

    function getCurrentList() {
        return $scope.lists[$scope.currentListIndex];
    }

    $scope.showMe = true;
    $scope.lists = [];
    $scope.allProducts = [];
    $scope.currentListIndex = 0;
    $scope.nameOfUser = HomeService.getNameOfUser();
    $scope.refresh(true);
})
.service('HomeService', function($http, $rootScope, Api, Session) {
    this.getNameOfUser = function () {
        return Session.user.name;
    }

    this.getAllProducts = function(hash) {
        var params = Api.getParams();
        params.cmd = 'get_all_products';
        params.user_id = Session.user.id;
        params.hash = hash;

        $http.get(Api.url, { params: params }).success(function(data) {
            if (data !== 'null')
                $rootScope.$broadcast('updateAllProducts', data);
        });
    };

    this.getListsWithProductsOfUser = function(hash) {
        var params = Api.getParams();
        params.cmd = 'get_lists_with_products_of_user';
        params.user_id = Session.user.id;
        params.hash = hash;

        return $http.get(Api.url, { params: params }).success(function(data) {
            if (data !== 'null')
                $rootScope.$broadcast('updateLists', data);
        });
    };

    this.addProductToList = function(product, list) {
        var params = Api.getParams();
        params.cmd = 'add_product_to_list';
        params.product_id = product.id;
        params.list_id = list.id;
        params.comment = product.comment;
        return $http.get(Api.url, { params: params });
    };

    this.addCustomProductToList = function(name, list) {
        var params = Api.getParams();
        params.cmd = 'add_custom_product_to_list';
        params.name = name;
        params.list_id = list.id;
        params.user_id = Session.user.id;
        return $http.get(Api.url, { params: params });
    };

    this.changeActiveStateOfProductFromList = function (product, list, active) {
        var params = Api.getParams();
        params.cmd = 'change_active_state_of_product_from_list';
        params.product_id = product.id;
        params.list_id = list.id;
        params.active = active;
        return $http.get(Api.url, { params: params });
    };

    this.addList = function(name) {
        var params = Api.getParams();
        params.cmd = 'add_list';
        params.name = name;
        params.user_id = Session.user.id;
        return $http.get(Api.url, { params: params });
    };

    this.moveListOnePosition = function(list_id, direction) {
        var params = Api.getParams();
        params.cmd = 'move_list_one_position';
        params.list_id = list_id;
        params.direction = direction;
        params.user_id = Session.user.id;
        return $http.get(Api.url, { params: params });
    };

    this.changeComment = function (product, list, comment) {
        var params = Api.getParams();
        params.cmd = 'change_comment';
        params.product_id = product.id;
        params.list_id = list.id;
        params.comment = comment;
        params.user_id = Session.user.id;
        return $http.get(Api.url, { params: params });
    };
})
.filter('productsFilter', function () {
    function fetchComment(query, pattern) {
        if (query.indexOf(' ' + pattern) !== -1)
            pattern = ' ' + pattern;
        return query.replace(pattern, '');
    }

    return function(items, query) {
        if (query === undefined || query === '')
            return items;

        var i, j, filteredItems = [];
        query = query.toLowerCase();

        for (i = 0; i < items.length; i++) {
            var itemName = items[i].name.toLowerCase();

            if (query.indexOf(itemName) !== -1) {
                items[i].comment = fetchComment(query, itemName);
                filteredItems.push(items[i]);
                continue;
            }

            var queryTokens = query.split(' ');
            for (j = 1; j <= queryTokens.length; j++) {
                var pattern = queryTokens.slice(-j).join(' ');
                if (itemName.startsWith(pattern)) {
                    items[i].comment = fetchComment(query, pattern);
                    filteredItems.push(items[i]);
                    break;
                }
            }
        }

        return filteredItems;
    };
})
;
<commit_msg>Bugfix: Wrong closing positions of parenthesis<commit_after>angular.module('shopper.home', [
    'ui.router'
])
.config(function config($stateProvider ) {
    $stateProvider.state('home', {
        url: '/home',
        views: {
            'main': {
                controller: 'HomeCtrl',
                templateUrl: 'home/home.tpl.html'
            }
        }
    });
})
.controller('HomeCtrl', function($scope, $rootScope, $timeout, $translate, $window, HomeService) {
    $scope.$on('bodyClicked', function(scope, event) {
        var productSearchQueryKey = 'productSearchQuery';
        if ([
            productSearchQueryKey,
            'add-custom-product'
        ].indexOf(event.target.id) !== -1)
            return;
        $scope[productSearchQueryKey] = '';
    });

    $scope.$on('updateAllProducts', function(scope, data) {
        $scope.allProducts = data;
    });

    $scope.$on('updateLists', function(scope, data) {
        $scope.lists = data;
    });

    $scope.$on('goToFirstList', function(scope) {
        if ($scope.currentListIndex > 0) {
            $scope.currentListIndex--;
        }
    });

    $scope.$on('hideListDetail', function(scope) {
        $scope.showMe = true;
    });

    $scope.$watch('currentListIndex', function(newValue) {
        if ($scope.lists[newValue]) {
            localStorage.lastListId = $scope.lists[newValue].id;
        }
    });

    $scope.refresh = function(initialLoad) {
        var allProductsSorted = [];
        var allProductsClean = JSON.parse(angular.toJson($scope.allProducts));
        for (var i = 0; i < allProductsClean.length; i++) {
            var arrayOfSortedKeyValues = [];
            var keys = Object.keys(allProductsClean[i]).sort();
            for (var j = 0; j < keys.length; j++) {
                arrayOfSortedKeyValues.push(allProductsClean[i][keys[j]]);
            }
            allProductsSorted.push(arrayOfSortedKeyValues);
        }
        var allProductsSortedJsonString = JSON.stringify(allProductsSorted);
        HomeService.getAllProducts(SparkMD5.hash(allProductsSortedJsonString));

        var listsClean = JSON.parse(angular.toJson($scope.lists));
        var listsStringified = JSON.stringify(listsClean);
        HomeService.getListsWithProductsOfUser(SparkMD5.hash(listsStringified)).then(function() {
            if (initialLoad) {
                $timeout(function() {
                    if (localStorage.lastListId) {
                        for (var i = 0; i < $scope.lists.length; i++) {
                            if ($scope.lists[i].id.toString() === localStorage.lastListId) {
                                $scope.currentListIndex = i;
                                break;
                            }
                        }
                    }
                });
            }
        });

        $timeout(function() {
            $scope.refresh();
        }, 1500);
    };

    $scope.addCustomProductToList = function(name) {
        if (name.trim() === '')
            return;

        HomeService.addCustomProductToList($scope.capitalizeString(name), $scope.lists[$scope.currentListIndex]).success(function(data) {
            $scope.refresh();
        });

        $scope.productSearchQuery = '';
    };

    $scope.isProductInList = function(product) {
        if (getCurrentList() === undefined) {
            return false;
        }

        for (var i = 0; i < getCurrentList().products.length; i++) {
            if (product.id === getCurrentList().products[i].id && getCurrentList().products[i].active) {
                return true;
            }
        }
        return false;
    };

    $scope.addProductToList = function(product) {
        if ($scope.isProductInList(product))
            return;

        HomeService.addProductToList(product, getCurrentList()).then(function() {
            $scope.refresh();
        });

        $scope.productSearchQuery = '';
    };

    $scope.changeActiveStateOfProductFromList = function(product, active) {
        HomeService.changeActiveStateOfProductFromList(product, getCurrentList(), active);
        getCurrentList().products[getCurrentList().products.indexOf(product)].disabled = true;
    };

    $scope.addList = function() {
        $translate('WHAT_SHOULD_WE_CALL_IT').then(function(translation) {
            var name = prompt(translation);
            if (! name)
                return;
            HomeService.addList(name).success(function(data) {
                HomeService.getListsWithProductsOfUser().success(function() {
                    $timeout(function() {
                        $scope.currentListIndex = $scope.lists.length - 1;
                    });
                });
            });
        });

    };

    $scope.editList = function(index) {
        $scope.showMe = false;
        $rootScope.$broadcast('showListDetail', $scope.lists[index]);
    };

    $scope.editComment = function (product, list) {
        $translate('CHANGE_COMMENT').then(function(translation) {
            $timeout(function() {
                var oldComment = (typeof product.comment === 'string') ? product.comment : '';
                var newComment = $window.prompt(translation, oldComment);
                if (newComment === null || newComment === oldComment)
                    return;
                product.disabled = true;
                HomeService.changeComment(product, list, newComment).then(function () {
                    $scope.refresh();
                });
            }, 0);
        });
    };

    $scope.moveListOnePosition = function(direction) {
        var currentListIndex = $scope.currentListIndex;
        var otherListIndex = $scope.currentListIndex + direction;

        if (otherListIndex < 0 || otherListIndex > $scope.lists.length - 1) {
            return;
        }

        $timeout(function() {
            $scope.currentListIndex = otherListIndex;
        });
        var otherList = $scope.lists[otherListIndex];
        $scope.lists[otherListIndex] = $scope.lists[currentListIndex];
        $scope.lists[currentListIndex] = otherList;

        HomeService.moveListOnePosition($scope.lists[otherListIndex].id, direction);
    };

    $scope.capitalizeString = function(str) {
        if (typeof str === 'string') {
            return str.charAt(0).toUpperCase() + str.slice(1);
        }
        return '';
    };

    $scope.isAProductName = function (searchQuery) {
        if (searchQuery === undefined)
            return false;
        searchQuery = searchQuery.toLowerCase();
        for (var i = 0; i < $scope.allProducts.length; i++) {
            if (searchQuery === $scope.allProducts[i].name.toLowerCase())
                return true;
        }
        return false;
    }

    function getCurrentList() {
        return $scope.lists[$scope.currentListIndex];
    }

    $scope.showMe = true;
    $scope.lists = [];
    $scope.allProducts = [];
    $scope.currentListIndex = 0;
    $scope.nameOfUser = HomeService.getNameOfUser();
    $scope.refresh(true);
})
.service('HomeService', function($http, $rootScope, Api, Session) {
    this.getNameOfUser = function () {
        return Session.user.name;
    }

    this.getAllProducts = function(hash) {
        var params = Api.getParams();
        params.cmd = 'get_all_products';
        params.user_id = Session.user.id;
        params.hash = hash;

        $http.get(Api.url, { params: params }).success(function(data) {
            if (data !== 'null')
                $rootScope.$broadcast('updateAllProducts', data);
        });
    };

    this.getListsWithProductsOfUser = function(hash) {
        var params = Api.getParams();
        params.cmd = 'get_lists_with_products_of_user';
        params.user_id = Session.user.id;
        params.hash = hash;

        return $http.get(Api.url, { params: params }).success(function(data) {
            if (data !== 'null')
                $rootScope.$broadcast('updateLists', data);
        });
    };

    this.addProductToList = function(product, list) {
        var params = Api.getParams();
        params.cmd = 'add_product_to_list';
        params.product_id = product.id;
        params.list_id = list.id;
        params.comment = product.comment;
        return $http.get(Api.url, { params: params });
    };

    this.addCustomProductToList = function(name, list) {
        var params = Api.getParams();
        params.cmd = 'add_custom_product_to_list';
        params.name = name;
        params.list_id = list.id;
        params.user_id = Session.user.id;
        return $http.get(Api.url, { params: params });
    };

    this.changeActiveStateOfProductFromList = function (product, list, active) {
        var params = Api.getParams();
        params.cmd = 'change_active_state_of_product_from_list';
        params.product_id = product.id;
        params.list_id = list.id;
        params.active = active;
        return $http.get(Api.url, { params: params });
    };

    this.addList = function(name) {
        var params = Api.getParams();
        params.cmd = 'add_list';
        params.name = name;
        params.user_id = Session.user.id;
        return $http.get(Api.url, { params: params });
    };

    this.moveListOnePosition = function(list_id, direction) {
        var params = Api.getParams();
        params.cmd = 'move_list_one_position';
        params.list_id = list_id;
        params.direction = direction;
        params.user_id = Session.user.id;
        return $http.get(Api.url, { params: params });
    };

    this.changeComment = function (product, list, comment) {
        var params = Api.getParams();
        params.cmd = 'change_comment';
        params.product_id = product.id;
        params.list_id = list.id;
        params.comment = comment;
        params.user_id = Session.user.id;
        return $http.get(Api.url, { params: params });
    };
})
.filter('productsFilter', function () {
    function fetchComment(query, pattern) {
        if (query.indexOf(' ' + pattern) !== -1)
            pattern = ' ' + pattern;
        return query.replace(pattern, '');
    }

    return function(items, query) {
        if (query === undefined || query === '')
            return items;

        var i, j, filteredItems = [];
        query = query.toLowerCase();

        for (i = 0; i < items.length; i++) {
            var itemName = items[i].name.toLowerCase();

            if (query.indexOf(itemName) !== -1) {
                items[i].comment = fetchComment(query, itemName);
                filteredItems.push(items[i]);
                continue;
            }

            var queryTokens = query.split(' ');
            for (j = 1; j <= queryTokens.length; j++) {
                var pattern = queryTokens.slice(-j).join(' ');
                if (itemName.startsWith(pattern)) {
                    items[i].comment = fetchComment(query, pattern);
                    filteredItems.push(items[i]);
                    break;
                }
            }
        }

        return filteredItems;
    };
})
;
<|endoftext|>