<commit_before>import {
  clearInput,
  getAllTabs,
  populateTabList,
  switchActiveTab,
  navigateResults,
  injectTabsInList,
} from './dom-utils';
import {
  deleteButton,
  searchInput,
  tabList,
} from './constants';
import filterResults from './search';

// Initialize tabs
populateTabList();

// Set a timeout on focus so input is focused on popup
document.addEventListener('DOMContentLoaded', () =>
  setTimeout(() => searchInput.focus(), 150),
);
// Any (non-navigating) keydown should activate the search field,
window.addEventListener('keydown', handleKeyDown);
searchInput.addEventListener('change', updateSearchResults);
searchInput.addEventListener('keyup', updateSearchResults);
deleteButton.addEventListener('click', (event) => {
  clearInput(event.target);
  tabList.childNodes[0].focus();
});

function handleKeyDown(event) {
  switch (event.key) {
    case 'Tab':
    case 'ArrowDown':
    case 'ArrowUp':
      event.preventDefault();
    case 'ArrowRight':
    case 'ArrowLeft':
      navigateResults(event.key);
      break;
    case 'Enter':
      event.preventDefault();
      if (
        !document.activeElement.className.includes('tab-object')
        && !document.activeElement.className.includes('no-result')
      ) {
        tabList.childNodes[0].focus();
        setTimeout(() => switchActiveTab(document.activeElement.dataset.id), 150);
      } else {
        switchActiveTab(document.activeElement.dataset.id);
      }
      break;
    case 'Escape':
      // This only works in chrome, in firefox it always closes the window
      if (searchInput.value.length === 0) {
        window.close();
      } else {
        clearInput();
      }
      break;
    default:
      searchInput.focus();
      break;
  }
}

function updateSearchResults(event) {
  // If input is empty hide the button
  if (searchInput.value.length === 0) {
    deleteButton.classList.add('hidden');
  } else {
    deleteButton.classList.remove('hidden');
  }

  const query = event.target.value.trim().toLowerCase();
  const getSearchResults = filterResults(query);
  return getAllTabs()
    .then(getSearchResults)
    .then(injectTabsInList);
}
<commit_msg>Fix tabList not populating on empty search fields<commit_after>import {
  clearInput,
  getAllTabs,
  populateTabList,
  switchActiveTab,
  navigateResults,
  injectTabsInList,
} from './dom-utils';
import {
  deleteButton,
  searchInput,
  tabList,
} from './constants';
import filterResults from './search';

// Initialize tabs
populateTabList();

// Set a timeout on focus so input is focused on popup
document.addEventListener('DOMContentLoaded', () =>
  setTimeout(() => searchInput.focus(), 150),
);
// Any (non-navigating) keydown should activate the search field,
window.addEventListener('keydown', handleKeyDown);
searchInput.addEventListener('change', updateSearchResults);
searchInput.addEventListener('keyup', updateSearchResults);
deleteButton.addEventListener('click', (event) => {
  clearInput(event.target);
  tabList.childNodes[0].focus();
});

function handleKeyDown(event) {
  switch (event.key) {
    case 'Tab':
    case 'ArrowDown':
    case 'ArrowUp':
      event.preventDefault();
    case 'ArrowRight':
    case 'ArrowLeft':
      navigateResults(event.key);
      break;
    case 'Enter':
      event.preventDefault();
      if (
        !document.activeElement.className.includes('tab-object')
        && !document.activeElement.className.includes('no-result')
      ) {
        tabList.childNodes[0].focus();
        setTimeout(() => switchActiveTab(document.activeElement.dataset.id), 150);
      } else {
        switchActiveTab(document.activeElement.dataset.id);
      }
      break;
    case 'Escape':
      // This only works in chrome, in firefox it always closes the window
      if (searchInput.value.length === 0) {
        window.close();
      } else {
        clearInput(searchInput);
      }
      break;
    default:
      searchInput.focus();
      break;
  }
}

function updateSearchResults(event) {
  const isSearchEmpty = searchInput.value.length === 0;
  // If input is empty hide the button
  if (isSearchEmpty) {
    deleteButton.classList.add('hidden');
  } else {
    deleteButton.classList.remove('hidden');
  }

  const query = event.target.value.trim().toLowerCase();
  const getSearchResults = isSearchEmpty
    ? x => x
    : filterResults(query);
  return getAllTabs()
    .then(getSearchResults)
    .then(injectTabsInList);
}
<|endoftext|>


<commit_before>import parser from 'posthtml-parser';
import render from 'posthtml-render';
import {walk} from 'posthtml/lib/api';
import rules from './rules.js';
import attrs from './attrs.js';
import tags from './tags.js';
import deepmerge from 'deepmerge';
import {js} from 'js-beautify';

const optionsDefault = {
  rules,
  attrs,
  tags,
  sync: false,
  commentFormat: true,
  mini: {
    removeAttribute: false
  },
  jsBeautifyOptions: {
    indent_size: 2, // eslint-disable-line camelcase
    jslint_happy: true // eslint-disable-line camelcase
  }
};

const COMMENT_START = '<!--';
const COMMENT_END = '-->';
const COMMENT_CONTENT_REG = new RegExp(`${COMMENT_START}([\\S\\s]*?)${COMMENT_END}`, 'g');

const nodeHasContent = (node, callback) => {
  if (
    typeof node === 'object' &&
    Object.prototype.hasOwnProperty.call(node, 'content')
  ) {
    node.content = callback(node.content);
  }
};

const nodeHasAttrs = (node, callback) => {
  if (
    typeof node === 'object' &&
    Object.prototype.hasOwnProperty.call(node, 'attrs')
  ) {
    node.attrs = Object.keys(node.attrs).reduce(callback, {});
  }
};

const getIndent = (level, {indent, eol}) => {
  const indentString = typeof indent === 'number' ? ' '.repeat(indent) : '\t';

  return `${eol}${indentString.repeat(level)}`;
};

const clean = tree =>
  parser(render(tree))
    .filter(node => {
      return (
        typeof node === 'object' ||
        (typeof node === 'string' &&
          (node.trim().length !== 0 || /doctype/gi.test(node)))
      );
    })
    .map(node => {
      if (Object.prototype.hasOwnProperty.call(node, 'content')) {
        node.content = clean(node.content);
      }

      return typeof node === 'string' ? node.trim() : node;
    });

const parseConditional = tree => {
  return tree.map(node => {
    nodeHasContent(node, parseConditional);

    if (typeof node === 'string' && /<!(?:--)?\[[\S\s]*?]>/.test(node)) {
      const conditional = /^((?:<[^>]+>)?<!(?:--)?\[[\S\s]*?]>(?:<!)?(?:-->)?)([\S\s]*?)(<!(?:--<!)?\[[\S\s]*?](?:--)?>)$/
        .exec(node)
        .slice(1)
        .map((node, index) =>
          index === 1 ?
            {tag: 'conditional-content', content: clean(parser(node))} :
            node
        );

      return {
        tag: 'conditional',
        content: conditional
      };
    }

    return node;
  });
};

const renderConditional = tree => {
  return tree.reduce((previousValue, node) => {
    if (
      typeof node === 'object' &&
      Object.prototype.hasOwnProperty.call(node, 'content')
    ) {
      node.content = renderConditional(node.content);
    }

    if (typeof node === 'object' && node.tag === 'conditional') {
      return [...previousValue, ...node.content];
    }

    if (typeof node === 'object' && node.tag === 'conditional-content') {
      node.tag = false;
      return [...previousValue, node];
    }

    return [...previousValue, node];
  }, []);
};

const indent = (tree, {rules: {indent, eol, blankLines, maxlen}}) => {
  const setIndent = (tree, level = 0) =>
    tree.reduce((previousValue, node, index) => {
      if (
        typeof node === 'object' &&
        Object.prototype.hasOwnProperty.call(node, 'content')
      ) {
        node.content = setIndent(node.content, ++level);
        --level;
      }

      if (tree.length === 1 && typeof tree[index] === 'string') {
        if (tree[index].length >= maxlen) {
          return [
            ...previousValue,
            getIndent(level, {indent, eol}),
            node,
            getIndent(--level, {indent, eol})
          ];
        }

        return [...previousValue, node];
      }

      if (level === 0 && tree.length - 1 === index && tree.length > 1) {
        return [...previousValue, getIndent(level, {indent, eol}), node];
      }

      if (level === 0 && tree.length - 1 === index && tree.length === 1) {
        return [...previousValue, node];
      }

      if (level === 0 && index === 0) {
        return [...previousValue, node, blankLines];
      }

      if (level === 0) {
        return [
          ...previousValue,
          getIndent(level, {indent, eol}),
          node,
          blankLines
        ];
      }

      if (tree.length - 1 === index) {
        return [
          ...previousValue,
          getIndent(level, {indent, eol}),
          node,
          getIndent(--level, {indent, eol})
        ];
      }

      if (typeof node === 'string' && /<!(?:--)?\[endif]*?]>/.test(node)) {
        return [
          ...previousValue,
          getIndent(level, {indent, eol}),
          node,
          blankLines
        ];
      }

      if (typeof node === 'string' && /<!(?:--)?\[[\S\s]*?]>/.test(node)) {
        return [...previousValue, getIndent(level, {indent, eol}), node];
      }

      if (node.tag === false) {
        return [...previousValue, ...node.content.slice(0, -1)];
      }

      return [
        ...previousValue,
        getIndent(level, {indent, eol}),
        node,
        blankLines
      ];
    }, []);

  return setIndent(tree);
};

const attributesBoolean = (tree, {attrs: {boolean}}) => {
  const removeAttributeValue = tree =>
    tree.map(node => {
      nodeHasContent(node, removeAttributeValue);

      if (
        typeof node === 'object' &&
        Object.prototype.hasOwnProperty.call(node, 'attrs')
      ) {
        Object.keys(node.attrs).forEach(key => {
          node.attrs[key] =
            boolean.includes(key) ||
            node.attrs[key]
              .trim()
              .split(' ')
              .filter(value => value.length)
              .join(' ');
        });
      }

      return node;
    });

  return removeAttributeValue(tree);
};

const lowerElementName = (tree, {tags}) => {
  tags = tags.map(({name}) => name);

  const bypass = tree =>
    tree.map(node => {
      nodeHasContent(node, bypass);

      if (
        typeof node === 'object' &&
        Object.prototype.hasOwnProperty.call(node, 'tag') &&
        tags.includes(node.tag.toLowerCase())
      ) {
        node.tag = node.tag.toLowerCase();
      }

      return node;
    });

  return bypass(tree);
};

const lowerAttributeName = tree => {
  const bypass = tree =>
    tree.map(node => {
      nodeHasContent(node, bypass);

      nodeHasAttrs(node, (previousValue, key) =>
        Object.assign(previousValue, {[key.toLowerCase()]: node.attrs[key]})
      );

      return node;
    });

  return bypass(tree);
};

const eof = (tree, {rules: {eof}}) => (eof ? [...tree, eof] : tree);

const mini = (tree, {mini}) => {
  const bypass = tree =>
    tree.map(node => {
      nodeHasContent(node, bypass);

      nodeHasAttrs(node, (previousValue, key) => {
        if (
          mini.removeAttribute &&
          mini.removeAttribute === 'empty' &&
          node.attrs[key].length === 0
        ) {
          return previousValue;
        }

        return Object.assign(previousValue, {
          [key.toLowerCase()]: node.attrs[key]
        });
      });

      return node;
    });

  return bypass(tree);
};

const sortLogic = function (key1, key2) {
  if (key1 < key2) {
    return -1;
  }

  if (key1 > key2) {
    return 1;
  }

  return 0;
};

const sortAttr = (tree, {rules: {sortAttr}}) => {
  tree.walk = walk;
  tree.walk(node => {
    if (sortAttr && node.attrs) {
      const keys = Object.keys(node.attrs);
      if (keys.length > 1) {
        node.attrs = keys
          .sort(sortLogic)
          .reduce(
            (current, key) =>
              Object.assign(current, {[key]: node.attrs[key]}),
            {}
          );
      }
    }

    return node;
  });

  delete tree.walk;
  return tree;
};

const jsPrettier = (tree, {rules: {indent, eol}, jsBeautifyOptions}) => {
  let level = 0;
  const prettier = tree =>
    tree.map(node => {
      ++level;
      nodeHasContent(node, prettier);

      if (node.tag === 'script') {
        const content = node.content ?
          [
            '\n',
            js(node.content.join(''), {
              ...jsBeautifyOptions,
              indent_level: level // eslint-disable-line camelcase
            }),
            getIndent(--level, {indent, eol})
          ] :
          [''];
        node.content = content;
      }

      --level;
      return node;
    });

  return prettier(tree);
};

const addLang = (tree, {rules: {lang}}) => {
  if (!lang) {
    return tree;
  }

  tree.walk = walk;

  tree.walk(node => {
    if (node.tag) {
      if (!node.attrs) {
        node.attrs = {lang};
        return node;
      }

      nodeHasAttrs(node, (previousValue, key) => {
        return Object.assign(
          {},
          {lang, [key]: node.attrs[key]},
          previousValue
        );
      });
    }

    return node;
  });

  delete tree.walk;
  return tree;
};

const commentsFormatting = (tree, {commentFormat}) => {
  if (!commentFormat) {
    return tree;
  }

  tree.walk = walk;
  tree.walk(node => {
    if (typeof node === 'string') {
      let [content] = node.match(COMMENT_CONTENT_REG)

      if (content === null) {
        return node;
      }

      return [
        COMMENT_START,
        ...content
          .trim()
          .split('\n')
          .filter(part => part.trim()),
        COMMENT_END
      ].join('\n')
    }

    return node;
  });

  delete tree.walk;
  return tree;
};

const beautify = (tree, options) =>
  [
    clean,
    parseConditional,
    renderConditional,
    commentsFormatting,
    indent,
    jsPrettier,
    lowerElementName,
    lowerAttributeName,
    attributesBoolean,
    sortAttr,
    addLang,
    eof,
    mini
  ].reduce(
    (previousValue, module) =>
      typeof module === 'function' ?
        module(previousValue, options) :
        previousValue,
    tree
  );

const normalize = (node, options) => {
  let {tree, api} = Object.keys(node).reduce(
    (scope, key) => {
      scope[Number.isNaN(Number(key)) ? 'api' : 'tree'][key] = node[key];

      return scope;
    },
    {tree: [], api: []}
  );

  tree = beautify(tree, deepmerge(optionsDefault, options));

  for (const [key, value] of Object.entries(api)) {
    tree[key] = value;
  }

  return tree;
};

export default (options = {}) => node => {
  if (!Array.isArray(node)) {
    throw new TypeError('tree is not Array');
  }

  if (node.length === 0) {
    return node;
  }

  return normalize(node, options);
};
<commit_msg>fix: get matching<commit_after>import parser from 'posthtml-parser';
import render from 'posthtml-render';
import {walk} from 'posthtml/lib/api';
import rules from './rules.js';
import attrs from './attrs.js';
import tags from './tags.js';
import deepmerge from 'deepmerge';
import {js} from 'js-beautify';

const optionsDefault = {
  rules,
  attrs,
  tags,
  sync: false,
  commentFormat: true,
  mini: {
    removeAttribute: false
  },
  jsBeautifyOptions: {
    indent_size: 2, // eslint-disable-line camelcase
    jslint_happy: true // eslint-disable-line camelcase
  }
};

const COMMENT_START = '<!--';
const COMMENT_END = '-->';
const COMMENT_CONTENT_REG = new RegExp(`${COMMENT_START}([\\S\\s]*?)${COMMENT_END}`, 'g');

const nodeHasContent = (node, callback) => {
  if (
    typeof node === 'object' &&
    Object.prototype.hasOwnProperty.call(node, 'content')
  ) {
    node.content = callback(node.content);
  }
};

const nodeHasAttrs = (node, callback) => {
  if (
    typeof node === 'object' &&
    Object.prototype.hasOwnProperty.call(node, 'attrs')
  ) {
    node.attrs = Object.keys(node.attrs).reduce(callback, {});
  }
};

const getIndent = (level, {indent, eol}) => {
  const indentString = typeof indent === 'number' ? ' '.repeat(indent) : '\t';

  return `${eol}${indentString.repeat(level)}`;
};

const clean = tree =>
  parser(render(tree))
    .filter(node => {
      return (
        typeof node === 'object' ||
        (typeof node === 'string' &&
          (node.trim().length !== 0 || /doctype/gi.test(node)))
      );
    })
    .map(node => {
      if (Object.prototype.hasOwnProperty.call(node, 'content')) {
        node.content = clean(node.content);
      }

      return typeof node === 'string' ? node.trim() : node;
    });

const parseConditional = tree => {
  return tree.map(node => {
    nodeHasContent(node, parseConditional);

    if (typeof node === 'string' && /<!(?:--)?\[[\S\s]*?]>/.test(node)) {
      const conditional = /^((?:<[^>]+>)?<!(?:--)?\[[\S\s]*?]>(?:<!)?(?:-->)?)([\S\s]*?)(<!(?:--<!)?\[[\S\s]*?](?:--)?>)$/
        .exec(node)
        .slice(1)
        .map((node, index) =>
          index === 1 ?
            {tag: 'conditional-content', content: clean(parser(node))} :
            node
        );

      return {
        tag: 'conditional',
        content: conditional
      };
    }

    return node;
  });
};

const renderConditional = tree => {
  return tree.reduce((previousValue, node) => {
    if (
      typeof node === 'object' &&
      Object.prototype.hasOwnProperty.call(node, 'content')
    ) {
      node.content = renderConditional(node.content);
    }

    if (typeof node === 'object' && node.tag === 'conditional') {
      return [...previousValue, ...node.content];
    }

    if (typeof node === 'object' && node.tag === 'conditional-content') {
      node.tag = false;
      return [...previousValue, node];
    }

    return [...previousValue, node];
  }, []);
};

const indent = (tree, {rules: {indent, eol, blankLines, maxlen}}) => {
  const setIndent = (tree, level = 0) =>
    tree.reduce((previousValue, node, index) => {
      if (
        typeof node === 'object' &&
        Object.prototype.hasOwnProperty.call(node, 'content')
      ) {
        node.content = setIndent(node.content, ++level);
        --level;
      }

      if (tree.length === 1 && typeof tree[index] === 'string') {
        if (tree[index].length >= maxlen) {
          return [
            ...previousValue,
            getIndent(level, {indent, eol}),
            node,
            getIndent(--level, {indent, eol})
          ];
        }

        return [...previousValue, node];
      }

      if (level === 0 && tree.length - 1 === index && tree.length > 1) {
        return [...previousValue, getIndent(level, {indent, eol}), node];
      }

      if (level === 0 && tree.length - 1 === index && tree.length === 1) {
        return [...previousValue, node];
      }

      if (level === 0 && index === 0) {
        return [...previousValue, node, blankLines];
      }

      if (level === 0) {
        return [
          ...previousValue,
          getIndent(level, {indent, eol}),
          node,
          blankLines
        ];
      }

      if (tree.length - 1 === index) {
        return [
          ...previousValue,
          getIndent(level, {indent, eol}),
          node,
          getIndent(--level, {indent, eol})
        ];
      }

      if (typeof node === 'string' && /<!(?:--)?\[endif]*?]>/.test(node)) {
        return [
          ...previousValue,
          getIndent(level, {indent, eol}),
          node,
          blankLines
        ];
      }

      if (typeof node === 'string' && /<!(?:--)?\[[\S\s]*?]>/.test(node)) {
        return [...previousValue, getIndent(level, {indent, eol}), node];
      }

      if (node.tag === false) {
        return [...previousValue, ...node.content.slice(0, -1)];
      }

      return [
        ...previousValue,
        getIndent(level, {indent, eol}),
        node,
        blankLines
      ];
    }, []);

  return setIndent(tree);
};

const attributesBoolean = (tree, {attrs: {boolean}}) => {
  const removeAttributeValue = tree =>
    tree.map(node => {
      nodeHasContent(node, removeAttributeValue);

      if (
        typeof node === 'object' &&
        Object.prototype.hasOwnProperty.call(node, 'attrs')
      ) {
        Object.keys(node.attrs).forEach(key => {
          node.attrs[key] =
            boolean.includes(key) ||
            node.attrs[key]
              .trim()
              .split(' ')
              .filter(value => value.length)
              .join(' ');
        });
      }

      return node;
    });

  return removeAttributeValue(tree);
};

const lowerElementName = (tree, {tags}) => {
  tags = tags.map(({name}) => name);

  const bypass = tree =>
    tree.map(node => {
      nodeHasContent(node, bypass);

      if (
        typeof node === 'object' &&
        Object.prototype.hasOwnProperty.call(node, 'tag') &&
        tags.includes(node.tag.toLowerCase())
      ) {
        node.tag = node.tag.toLowerCase();
      }

      return node;
    });

  return bypass(tree);
};

const lowerAttributeName = tree => {
  const bypass = tree =>
    tree.map(node => {
      nodeHasContent(node, bypass);

      nodeHasAttrs(node, (previousValue, key) =>
        Object.assign(previousValue, {[key.toLowerCase()]: node.attrs[key]})
      );

      return node;
    });

  return bypass(tree);
};

const eof = (tree, {rules: {eof}}) => (eof ? [...tree, eof] : tree);

const mini = (tree, {mini}) => {
  const bypass = tree =>
    tree.map(node => {
      nodeHasContent(node, bypass);

      nodeHasAttrs(node, (previousValue, key) => {
        if (
          mini.removeAttribute &&
          mini.removeAttribute === 'empty' &&
          node.attrs[key].length === 0
        ) {
          return previousValue;
        }

        return Object.assign(previousValue, {
          [key.toLowerCase()]: node.attrs[key]
        });
      });

      return node;
    });

  return bypass(tree);
};

const sortLogic = function (key1, key2) {
  if (key1 < key2) {
    return -1;
  }

  if (key1 > key2) {
    return 1;
  }

  return 0;
};

const sortAttr = (tree, {rules: {sortAttr}}) => {
  tree.walk = walk;
  tree.walk(node => {
    if (sortAttr && node.attrs) {
      const keys = Object.keys(node.attrs);
      if (keys.length > 1) {
        node.attrs = keys
          .sort(sortLogic)
          .reduce(
            (current, key) =>
              Object.assign(current, {[key]: node.attrs[key]}),
            {}
          );
      }
    }

    return node;
  });

  delete tree.walk;
  return tree;
};

const jsPrettier = (tree, {rules: {indent, eol}, jsBeautifyOptions}) => {
  let level = 0;
  const prettier = tree =>
    tree.map(node => {
      ++level;
      nodeHasContent(node, prettier);

      if (node.tag === 'script') {
        const content = node.content ?
          [
            '\n',
            js(node.content.join(''), {
              ...jsBeautifyOptions,
              indent_level: level // eslint-disable-line camelcase
            }),
            getIndent(--level, {indent, eol})
          ] :
          [''];
        node.content = content;
      }

      --level;
      return node;
    });

  return prettier(tree);
};

const addLang = (tree, {rules: {lang}}) => {
  if (!lang) {
    return tree;
  }

  tree.walk = walk;

  tree.walk(node => {
    if (node.tag) {
      if (!node.attrs) {
        node.attrs = {lang};
        return node;
      }

      nodeHasAttrs(node, (previousValue, key) => {
        return Object.assign(
          {},
          {lang, [key]: node.attrs[key]},
          previousValue
        );
      });
    }

    return node;
  });

  delete tree.walk;
  return tree;
};

const commentsFormatting = (tree, {commentFormat}) => {
  if (!commentFormat) {
    return tree;
  }

  tree.walk = walk;
  tree.walk(node => {
    if (typeof node === 'string') {
      const contentMatch = node.match(COMMENT_CONTENT_REG)

      if (contentMatch === null) {
        return node;
      }

      const content = contentMatch[0]
          .trim()
          .split('\n')
          .filter(part => part.trim());

      return [
        COMMENT_START,
        ...content,
        COMMENT_END
      ].join('\n')
    }

    return node;
  });

  delete tree.walk;
  return tree;
};

const beautify = (tree, options) =>
  [
    clean,
    parseConditional,
    renderConditional,
    commentsFormatting,
    indent,
    jsPrettier,
    lowerElementName,
    lowerAttributeName,
    attributesBoolean,
    sortAttr,
    addLang,
    eof,
    mini
  ].reduce(
    (previousValue, module) =>
      typeof module === 'function' ?
        module(previousValue, options) :
        previousValue,
    tree
  );

const normalize = (node, options) => {
  let {tree, api} = Object.keys(node).reduce(
    (scope, key) => {
      scope[Number.isNaN(Number(key)) ? 'api' : 'tree'][key] = node[key];

      return scope;
    },
    {tree: [], api: []}
  );

  tree = beautify(tree, deepmerge(optionsDefault, options));

  for (const [key, value] of Object.entries(api)) {
    tree[key] = value;
  }

  return tree;
};

export default (options = {}) => node => {
  if (!Array.isArray(node)) {
    throw new TypeError('tree is not Array');
  }

  if (node.length === 0) {
    return node;
  }

  return normalize(node, options);
};
<|endoftext|>


<commit_before>/*
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import {MatrixClientPeg} from './MatrixClientPeg';
import dis from "./dispatcher";
import Modal from './Modal';
import * as sdk from './index';
import { _t } from './languageHandler';
import {RIGHT_PANEL_PHASES} from "./stores/RightPanelStorePhases";
import {findDMForUser} from './createRoom';
import {accessSecretStorage} from './CrossSigningManager';
import SettingsStore from './settings/SettingsStore';

export async function enable4SIfNeeded() {
    const cli = MatrixClientPeg.get();
    if (!cli.isCryptoEnabled() || !SettingsStore.isFeatureEnabled("feature_cross_signing")) {
        return false;
    }
    const masterPK = cli.getCrossSigningId();
    if (!masterPK) {
        await accessSecretStorage();
        return false;
    }

    return true;
}

export async function verifyDevice(user, device) {
    if (!await enable4SIfNeeded()) {
        return;
    }
    const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
    Modal.createTrackedDialog("Verification warning", "unverified session", QuestionDialog, {
        headerImage: require("../res/img/e2e/warning.svg"),
        title: _t("Not Trusted"),
        description:

{_t("%(name)s (%(userId)s) signed in to a new session without verifying it:", {name: user.displayName, userId: user.userId})}

{device.getDisplayName()} ({device.deviceId})

{_t("Ask this user to verify their session, or manually verify it below.")}

,
        onFinished: async (doneClicked) => {
            const manuallyVerifyClicked = !doneClicked;
            if (!manuallyVerifyClicked) {
                return;
            }
            const cli = MatrixClientPeg.get();
            const verificationRequestPromise = cli.requestVerification(
                user.userId,
                [device.deviceId],
            );
            dis.dispatch({
                action: "set_right_panel_phase",
                phase: RIGHT_PANEL_PHASES.EncryptionPanel,
                refireParams: {member: user, verificationRequestPromise},
            });
        },
        primaryButton: _t("Done"),
        cancelButton: _t("Manually Verify"),
    });
}

export async function legacyVerifyUser(user) {
    if (!await enable4SIfNeeded()) {
        return;
    }
    const cli = MatrixClientPeg.get();
    const verificationRequestPromise = cli.requestVerification(user.userId);
    dis.dispatch({
        action: "set_right_panel_phase",
        phase: RIGHT_PANEL_PHASES.EncryptionPanel,
        refireParams: {member: user, verificationRequestPromise},
    });
}

export async function verifyUser(user) {
    if (!await enable4SIfNeeded()) {
        return;
    }
    const cli = MatrixClientPeg.get();
    const dmRoom = findDMForUser(cli, user.userId);
    let existingRequest;
    if (dmRoom) {
        existingRequest = cli.findVerificationRequestDMInProgress(dmRoom.roomId);
    }
    dis.dispatch({
        action: "set_right_panel_phase",
        phase: RIGHT_PANEL_PHASES.EncryptionPanel,
        refireParams: {
            member: user,
            verificationRequest: existingRequest,
        },
    });
}
<commit_msg>use USK as we'll only have that around when using key sharing<commit_after>/*
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import {MatrixClientPeg} from './MatrixClientPeg';
import dis from "./dispatcher";
import Modal from './Modal';
import * as sdk from './index';
import { _t } from './languageHandler';
import {RIGHT_PANEL_PHASES} from "./stores/RightPanelStorePhases";
import {findDMForUser} from './createRoom';
import {accessSecretStorage} from './CrossSigningManager';
import SettingsStore from './settings/SettingsStore';

export async function enable4SIfNeeded() {
    const cli = MatrixClientPeg.get();
    if (!cli.isCryptoEnabled() || !SettingsStore.isFeatureEnabled("feature_cross_signing")) {
        return false;
    }
    const usk = cli.getCrossSigningId("user_signing");
    if (!usk) {
        await accessSecretStorage();
        return false;
    }

    return true;
}

export async function verifyDevice(user, device) {
    if (!await enable4SIfNeeded()) {
        return;
    }
    const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
    Modal.createTrackedDialog("Verification warning", "unverified session", QuestionDialog, {
        headerImage: require("../res/img/e2e/warning.svg"),
        title: _t("Not Trusted"),
        description:

{_t("%(name)s (%(userId)s) signed in to a new session without verifying it:", {name: user.displayName, userId: user.userId})}

{device.getDisplayName()} ({device.deviceId})

{_t("Ask this user to verify their session, or manually verify it below.")}

,
        onFinished: async (doneClicked) => {
            const manuallyVerifyClicked = !doneClicked;
            if (!manuallyVerifyClicked) {
                return;
            }
            const cli = MatrixClientPeg.get();
            const verificationRequestPromise = cli.requestVerification(
                user.userId,
                [device.deviceId],
            );
            dis.dispatch({
                action: "set_right_panel_phase",
                phase: RIGHT_PANEL_PHASES.EncryptionPanel,
                refireParams: {member: user, verificationRequestPromise},
            });
        },
        primaryButton: _t("Done"),
        cancelButton: _t("Manually Verify"),
    });
}

export async function legacyVerifyUser(user) {
    if (!await enable4SIfNeeded()) {
        return;
    }
    const cli = MatrixClientPeg.get();
    const verificationRequestPromise = cli.requestVerification(user.userId);
    dis.dispatch({
        action: "set_right_panel_phase",
        phase: RIGHT_PANEL_PHASES.EncryptionPanel,
        refireParams: {member: user, verificationRequestPromise},
    });
}

export async function verifyUser(user) {
    if (!await enable4SIfNeeded()) {
        return;
    }
    const cli = MatrixClientPeg.get();
    const dmRoom = findDMForUser(cli, user.userId);
    let existingRequest;
    if (dmRoom) {
        existingRequest = cli.findVerificationRequestDMInProgress(dmRoom.roomId);
    }
    dis.dispatch({
        action: "set_right_panel_phase",
        phase: RIGHT_PANEL_PHASES.EncryptionPanel,
        refireParams: {
            member: user,
            verificationRequest: existingRequest,
        },
    });
}
<|endoftext|>


<commit_before>import VglNamespace from './vgl-namespace.js';
import { WebGLRenderer } from './three.js';
import { boolean, string } from './validators.js';
import { cameras, scenes } from './object-stores.js';

function resizeCamera(camera, domElement) {
  const width = domElement.clientWidth;
  const height = domElement.clientHeight;
  if (camera.isPerspectiveCamera) {
    Object.assign(camera, { aspect: width / height });
  } else { // isOrthographicCamera
    Object.assign(camera, {
      left: width / -2,
      right: width / 2,
      top: height / 2,
      bottom: height / -2,
    });
  }
  camera.updateProjectionMatrix();
}

function resizeRenderer(renderer, domElement) {
  renderer.setSize(domElement.clientWidth, domElement.clientHeight, false);
}

export default {
  mixins: [VglNamespace],
  props: {
    precision: string,
    alpha: boolean,
    disablePremultipliedAlpha: boolean,
    antialias: boolean,
    disableStencil: boolean,
    preserveDrawingBuffer: boolean,
    disableDepth: boolean,
    logarithmicDepthBuffer: boolean,
    camera: string,
    scene: string,
    shadowMapEnabled: boolean,
  },
  provide() {
    return {
      vglUpdate: this.render,
    };
  },
  data() {
    return {
      req: true,
    };
  },
  computed: {
    inst() {
      const inst = new WebGLRenderer({
        precision: this.precision,
        alpha: this.alpha,
        premultipliedAlpha: !this.disablePremultipliedAlpha,
        antialias: this.antialias,
        stencil: !this.disableStencil,
        preserveDrawingBuffer: this.preserveDrawingBuffer,
        depth: !this.disableDepth,
        logarithmicDepthBuffer: this.logarithmicDepthBuffer,
      });
      inst.shadowMap.enabled = this.shadowMapEnabled;
      return inst;
    },
    cmr() {
      return cameras[this.vglCameras.forGet[this.camera]];
    },
    scn() {
      return scenes[this.vglScenes.forGet[this.scene]];
    },
  },
  methods: {
    resize() {
      resizeRenderer(this.inst, this.$el);
      if (this.cmr) {
        resizeCamera(this.cmr, this.$el);
        if (this.scn) this.render();
      }
    },
    render() {
      if (this.req) {
        this.$nextTick(() => {
          if (this.scn && this.cmr) {
            this.inst.render(this.scn, this.cmr);
          }
          this.req = true;
        });
        this.req = false;
      }
    },
  },
  watch: {
    inst(inst, oldInst) {
      if (this.$el) this.$el.replaceChild(inst.domElement, oldInst.domElement);
      oldInst.dispose();
    },
    scn(scn, oldScn) {
      if (oldScn) oldScn.removeEventListener('update', this.render);
      if (scn) {
        scn.addEventListener('update', this.render);
        this.render();
      }
    },
    cmr(cmr, oldCmr) {
      if (oldCmr) oldCmr.removeEventListener('update', this.render);
      if (cmr) {
        cmr.addEventListener('update', this.render);
        resizeCamera(cmr, this.$el);
        this.render();
      }
    },
  },
  created() {
    if (this.scn) this.scn.addEventListener('update', this.render);
    if (this.cmr) this.cmr.addEventListener('update', this.render);
  },
  mounted() {
    this.$el.insertBefore(this.inst.domElement, this.$el.firstChild);
    this.resize();
  },
  render(h) {
    return h('div', [
      h('iframe', {
        ref: 'frm',
        style: {
          visibility: 'hidden',
          width: '100%',
          height: '100%',
        },
        on: {
          load: (evt) => {
            evt.target.contentWindow.addEventListener('resize', this.resize, false);
          },
        },
      }, this.$slots.default),
    ]);
  },
};
<commit_msg>refactor: req variable does not need to be reactive data.<commit_after>import VglNamespace from './vgl-namespace.js';
import { WebGLRenderer } from './three.js';
import { boolean, string } from './validators.js';
import { cameras, scenes } from './object-stores.js';

function resizeCamera(camera, domElement) {
  const width = domElement.clientWidth;
  const height = domElement.clientHeight;
  if (camera.isPerspectiveCamera) {
    Object.assign(camera, { aspect: width / height });
  } else { // isOrthographicCamera
    Object.assign(camera, {
      left: width / -2,
      right: width / 2,
      top: height / 2,
      bottom: height / -2,
    });
  }
  camera.updateProjectionMatrix();
}

function resizeRenderer(renderer, domElement) {
  renderer.setSize(domElement.clientWidth, domElement.clientHeight, false);
}

export default {
  mixins: [VglNamespace],
  props: {
    precision: string,
    alpha: boolean,
    disablePremultipliedAlpha: boolean,
    antialias: boolean,
    disableStencil: boolean,
    preserveDrawingBuffer: boolean,
    disableDepth: boolean,
    logarithmicDepthBuffer: boolean,
    camera: string,
    scene: string,
    shadowMapEnabled: boolean,
  },
  provide() {
    return {
      vglUpdate: this.render,
    };
  },
  computed: {
    inst() {
      const inst = new WebGLRenderer({
        precision: this.precision,
        alpha: this.alpha,
        premultipliedAlpha: !this.disablePremultipliedAlpha,
        antialias: this.antialias,
        stencil: !this.disableStencil,
        preserveDrawingBuffer: this.preserveDrawingBuffer,
        depth: !this.disableDepth,
        logarithmicDepthBuffer: this.logarithmicDepthBuffer,
      });
      inst.shadowMap.enabled = this.shadowMapEnabled;
      return inst;
    },
    cmr() {
      return cameras[this.vglCameras.forGet[this.camera]];
    },
    scn() {
      return scenes[this.vglScenes.forGet[this.scene]];
    },
  },
  methods: {
    resize() {
      resizeRenderer(this.inst, this.$el);
      if (this.cmr) {
        resizeCamera(this.cmr, this.$el);
        if (this.scn) this.render();
      }
    },
    render: (() => {
      let req = true;
      return function render() {
        if (req) {
          this.$nextTick(() => {
            if (this.scn && this.cmr) {
              this.inst.render(this.scn, this.cmr);
            }
            req = true;
          });
          req = false;
        }
      };
    })(),
  },
  watch: {
    inst(inst, oldInst) {
      if (this.$el) this.$el.replaceChild(inst.domElement, oldInst.domElement);
      oldInst.dispose();
    },
    scn(scn, oldScn) {
      if (oldScn) oldScn.removeEventListener('update', this.render);
      if (scn) {
        scn.addEventListener('update', this.render);
        this.render();
      }
    },
    cmr(cmr, oldCmr) {
      if (oldCmr) oldCmr.removeEventListener('update', this.render);
      if (cmr) {
        cmr.addEventListener('update', this.render);
        resizeCamera(cmr, this.$el);
        this.render();
      }
    },
  },
  created() {
    if (this.scn) this.scn.addEventListener('update', this.render);
    if (this.cmr) this.cmr.addEventListener('update', this.render);
  },
  mounted() {
    this.$el.insertBefore(this.inst.domElement, this.$el.firstChild);
    this.resize();
  },
  render(h) {
    return h('div', [
      h('iframe', {
        ref: 'frm',
        style: {
          visibility: 'hidden',
          width: '100%',
          height: '100%',
        },
        on: {
          load: (evt) => {
            evt.target.contentWindow.addEventListener('resize', this.resize, false);
          },
        },
      }, this.$slots.default),
    ]);
  },
};
<|endoftext|>


<commit_before>import {Container} from 'aurelia-dependency-injection';
import {View} from './view';
import {ViewSlot} from './view-slot';
import {ContentSelector} from './content-selector';
import {ViewResources} from './resource-registry';

class BehaviorContainer extends Container {
  get(key){
    if(key === Element){
      return this.element;
    }

    if(key === BoundViewFactory){
      return this.boundViewFactory || (this.boundViewFactory = new BoundViewFactory(this, this.instruction.viewFactory, this.executionContext));
    }

    if(key === ViewSlot){
      if(this.viewSlot === undefined){
        this.viewSlot = new ViewSlot(this.element, this.instruction.anchorIsContainer, this.executionContext);
        this.children.push(this.viewSlot);
      }

      return this.viewSlot;
    }

    if(key === ViewResources){
      return this.viewResources;
    }

    return super.get(key);
  }
}

function applyInstructions(containers, executionContext, element, instruction,
  behaviors, bindings, children, contentSelectors, resources){
  var behaviorInstructions = instruction.behaviorInstructions,
      elementContainer;

  if(instruction.contentExpression){
    bindings.push(instruction.contentExpression.createBinding(element.nextSibling));
    element.parentNode.removeChild(element);
    return;
  }

  if(instruction.contentSelector){
    contentSelectors.push(new ContentSelector(element, instruction.selector));
    return;
  }

  if(behaviorInstructions.length){
    containers[instruction.injectorId] = elementContainer = containers[instruction.parentInjectorId].createTypedChild(BehaviorContainer);

    elementContainer.element = element;
    elementContainer.instruction = instruction;
    elementContainer.executionContext = executionContext;
    elementContainer.children = children;
    elementContainer.viewResources = resources;
    elementContainer.autoRegisterAll(instruction.providers);

    behaviorInstructions.forEach(behaviorInstruction => {
      var instance = behaviorInstruction.type.create(elementContainer, behaviorInstruction, element, bindings);

      if(instance.contentView){
        children.push(instance.contentView);
      }

      behaviors.push(instance);
    });
  }

  instruction.expressions.forEach(exp => bindings.push(exp.createBinding(element)));
}

export class BoundViewFactory {
  constructor(parentContainer, viewFactory, executionContext){
    this.parentContainer = parentContainer;
    this.viewFactory = viewFactory;
    this.executionContext = executionContext;
    this.factoryOptions = { behaviorInstance:false };
  }

  create(executionContext){
    var childContainer = this.parentContainer.createChild(),
        context = executionContext || this.executionContext;

    this.factoryOptions.systemControlled = !executionContext;

    return this.viewFactory.create(childContainer, context, this.factoryOptions);
  }
}

var defaultFactoryOptions = {
  systemControlled:false,
  suppressBind:false
};

export class ViewFactory{
  constructor(template, instructions, resources){
    this.template = template;
    this.instructions = instructions;
    this.resources = resources;
  }

  create(container, executionContext, options=defaultFactoryOptions){
    var fragment = this.template.cloneNode(true),
        instructables = fragment.querySelectorAll('.au-target'),
        instructions = this.instructions,
        resources = this.resources,
        behaviors = [],
        bindings = [],
        children = [],
        contentSelectors = [],
        containers = { root:container };

    for(var i = 0, ii = instructables.length; i < ii; i++){
      applyInstructions(containers, executionContext, instructables[i],
        instructions[i], behaviors, bindings, children, contentSelectors, resources);
    }

    var view = new View(fragment, behaviors, bindings, children, options.systemControlled, contentSelectors);

    view.created(executionContext);

    if(!options.suppressBind){
      view.bind(executionContext);
    }

    return view;
  }
}<commit_msg>perf(view-factory): loop optimization<commit_after>import {Container} from 'aurelia-dependency-injection';
import {View} from './view';
import {ViewSlot} from './view-slot';
import {ContentSelector} from './content-selector';
import {ViewResources} from './resource-registry';

class BehaviorContainer extends Container {
  get(key){
    if(key === Element){
      return this.element;
    }

    if(key === BoundViewFactory){
      return this.boundViewFactory || (this.boundViewFactory = new BoundViewFactory(this, this.instruction.viewFactory, this.executionContext));
    }

    if(key === ViewSlot){
      if(this.viewSlot === undefined){
        this.viewSlot = new ViewSlot(this.element, this.instruction.anchorIsContainer, this.executionContext);
        this.children.push(this.viewSlot);
      }

      return this.viewSlot;
    }

    if(key === ViewResources){
      return this.viewResources;
    }

    return super.get(key);
  }
}

function applyInstructions(containers, executionContext, element, instruction,
  behaviors, bindings, children, contentSelectors, resources){
  var behaviorInstructions = instruction.behaviorInstructions,
      expressions = instruction.expressions,
      elementContainer, i, ii, providers, current, instance;

  if(instruction.contentExpression){
    bindings.push(instruction.contentExpression.createBinding(element.nextSibling));
    element.parentNode.removeChild(element);
    return;
  }

  if(instruction.contentSelector){
    contentSelectors.push(new ContentSelector(element, instruction.selector));
    return;
  }

  if(behaviorInstructions.length){
    containers[instruction.injectorId] = elementContainer = containers[instruction.parentInjectorId].createTypedChild(BehaviorContainer);

    elementContainer.element = element;
    elementContainer.instruction = instruction;
    elementContainer.executionContext = executionContext;
    elementContainer.children = children;
    elementContainer.viewResources = resources;

    providers = instruction.providers;
    i = providers.length;

    while(i--) {
      elementContainer.registerSingleton(providers[i]);
    }

    for(i = 0, ii = behaviorInstructions.length; i < ii; ++i){
      current = behaviorInstructions[i];
      instance = current.type.create(elementContainer, current, element, bindings);

      if(instance.contentView){
        children.push(instance.contentView);
      }

      behaviors.push(instance);
    }
  }

  for(i = 0, i = expressions.length; i < ii; ++i){
    bindings.push(expressions[i].createBinding(element));
  }
}

export class BoundViewFactory {
  constructor(parentContainer, viewFactory, executionContext){
    this.parentContainer = parentContainer;
    this.viewFactory = viewFactory;
    this.executionContext = executionContext;
    this.factoryOptions = { behaviorInstance:false };
  }

  create(executionContext){
    var childContainer = this.parentContainer.createChild(),
        context = executionContext || this.executionContext;

    this.factoryOptions.systemControlled = !executionContext;

    return this.viewFactory.create(childContainer, context, this.factoryOptions);
  }
}

var defaultFactoryOptions = {
  systemControlled:false,
  suppressBind:false
};

export class ViewFactory{
  constructor(template, instructions, resources){
    this.template = template;
    this.instructions = instructions;
    this.resources = resources;
  }

  create(container, executionContext, options=defaultFactoryOptions){
    var fragment = this.template.cloneNode(true),
        instructables = fragment.querySelectorAll('.au-target'),
        instructions = this.instructions,
        resources = this.resources,
        behaviors = [],
        bindings = [],
        children = [],
        contentSelectors = [],
        containers = { root:container },
        i, ii, view;

    for(i = 0, ii = instructables.length; i < ii; ++i){
      applyInstructions(containers, executionContext, instructables[i],
        instructions[i], behaviors, bindings, children, contentSelectors, resources);
    }

    view = new View(fragment, behaviors, bindings, children, options.systemControlled, contentSelectors);
    view.created(executionContext);

    if(!options.suppressBind){
      view.bind(executionContext);
    }

    return view;
  }
}<|endoftext|>


<commit_before>/**
 * Copyright (c) Baidu Inc. All rights reserved.
 *
 * This source code is licensed under the MIT license.
 * See LICENSE file in the project root for license information.
 *
 * @file 元素节点类
 */

var guid = require('../util/guid');
var changeExprCompare = require('../runtime/change-expr-compare');
var changesIsInDataRef = require('../runtime/changes-is-in-data-ref');
var evalExpr = require('../runtime/eval-expr');
var insertBefore = require('../browser/insert-before');
var LifeCycle = require('./life-cycle');
var NodeType = require('./node-type');
var reverseElementChildren = require('./reverse-element-children');
var isDataChangeByElement = require('./is-data-change-by-element');
var getPropHandler = require('./get-prop-handler');
var createNode = require('./create-node');
var elementOwnDetach = require('./element-own-detach');
var elementOwnDispose = require('./element-own-dispose');
var elementOwnOnEl = require('./element-own-on-el');
var elementOwnAttached = require('./element-own-attached');
var nodeSBindInit = require('./node-s-bind-init');
var nodeSBindUpdate = require('./node-s-bind-update');
var warnSetHTML = require('./warn-set-html');
var getNodePath = require('./get-node-path');

/**
 * 元素节点类
 *
 * @class
 * @param {Object} aNode 抽象节点
 * @param {Node} parent 父亲节点
 * @param {Model} scope 所属数据环境
 * @param {Component} owner 所属组件环境
 * @param {DOMChildrenWalker?} reverseWalker 子元素遍历对象
 */
function Element(aNode, parent, scope, owner, reverseWalker) {
    this.aNode = aNode;
    this.owner = owner;
    this.scope = scope;
    this.parent = parent;

    this.lifeCycle = LifeCycle.start;
    this.children = [];
    this._elFns = [];
    this.parentComponent = parent.nodeType === NodeType.CMPT
        ? parent
        : parent.parentComponent;

    this.tagName = aNode.tagName;

    // #[begin] allua
    // ie8- 不支持innerHTML输出自定义标签
    /* istanbul ignore if */
    if (ieOldThan9 && this.tagName.indexOf('-') > 0) {
        this.tagName = 'div';
    }
    // #[end]

    nodeSBindInit(this, aNode.directives.bind);
    this.lifeCycle = LifeCycle.inited;

    // #[begin] reverse
    if (reverseWalker) {
        var currentNode = reverseWalker.current;

        /* istanbul ignore if */
        if (!currentNode) {
            throw new Error('[SAN REVERSE ERROR] Element not found. \nPaths: '
                + getNodePath(this).join(' > '));
        }

        /* istanbul ignore if */
        if (currentNode.nodeType !== 1) {
            throw new Error('[SAN REVERSE ERROR] Element type not match, expect 1 but '
                + currentNode.nodeType + '.\nPaths: '
                + getNodePath(this).join(' > '));
        }

        /* istanbul ignore if */
        if (currentNode.tagName.toLowerCase() !== this.tagName) {
            throw new Error('[SAN REVERSE ERROR] Element tagName not match, expect '
                + this.tagName + ' but meat ' + currentNode.tagName.toLowerCase() + '.\nPaths: '
                + getNodePath(this).join(' > '));
        }

        this.el = currentNode;
        reverseWalker.goNext();

        reverseElementChildren(this, this.scope, this.owner);

        this.lifeCycle = LifeCycle.created;
        this._attached();
        this.lifeCycle = LifeCycle.attached;
    }
    // #[end]
}

Element.prototype.nodeType = NodeType.ELEM;

/**
 * 将元素attach到页面
 *
 * @param {HTMLElement} parentEl 要添加到的父元素
 * @param {HTMLElement＝} beforeEl 要添加到哪个元素之前
 */
Element.prototype.attach = function (parentEl, beforeEl) {
    if (!this.lifeCycle.attached) {
        if (!this.el) {
            var sourceNode = this.aNode.hotspot.sourceNode;
            var props = this.aNode.props;

            if (sourceNode) {
                this.el = sourceNode.cloneNode(false);
                props = this.aNode.hotspot.dynamicProps;
            }
            else {
                this.el = createEl(this.tagName);
            }

            if (this._sbindData) {
                for (var key in this._sbindData) {
                    if (this._sbindData.hasOwnProperty(key)) {
                        getPropHandler(this.tagName, key)(
                            this.el,
                            this._sbindData[key],
                            key,
                            this
                        );
                    }
                }
            }

            for (var i = 0, l = props.length; i < l; i++) {
                var prop = props[i];
                var propName = prop.name;
                var value = evalExpr(prop.expr, this.scope, this.owner);

                if (value || !emptyPropWhenCreate[propName]) {
                    prop.handler(this.el, value, propName, this, prop);
                }
            }

            this.lifeCycle = LifeCycle.created;
        }
        insertBefore(this.el, parentEl, beforeEl);

        if (!this._contentReady) {
            var htmlDirective = this.aNode.directives.html;

            if (htmlDirective) {
                // #[begin] error
                warnSetHTML(this.el);
                // #[end]

                this.el.innerHTML = evalExpr(htmlDirective.value, this.scope, this.owner);
            }
            else {
                for (var i = 0, l = this.aNode.children.length; i < l; i++) {
                    var childANode = this.aNode.children[i];
                    var child = childANode.Clazz
                        ? new childANode.Clazz(childANode, this, this.scope, this.owner)
                        : createNode(childANode, this, this.scope, this.owner);
                    this.children.push(child);
                    child.attach(this.el);
                }
            }

            this._contentReady = 1;
        }

        this._attached();

        this.lifeCycle = LifeCycle.attached;
    }
};

Element.prototype.detach = elementOwnDetach;
Element.prototype.dispose = elementOwnDispose;
Element.prototype._onEl = elementOwnOnEl;
Element.prototype._leave = function () {
    if (this.leaveDispose) {
        if (!this.lifeCycle.disposed) {
            var len = this.children.length;
            while (len--) {
                this.children[len].dispose(1, 1);
            }

            len = this._elFns.length;
            while (len--) {
                var fn = this._elFns[len];
                un(this.el, fn[0], fn[1], fn[2]);
            }
            this._elFns = null;

            // #[begin] allua
            /* istanbul ignore if */
            if (this._inputTimer) {
                clearInterval(this._inputTimer);
                this._inputTimer = null;
            }
            // #[end]

            // 如果没有parent，说明是一个root component，一定要从dom树中remove
            if (!this.disposeNoDetach || !this.parent) {
                removeEl(this.el);
            }

            this.lifeCycle = LifeCycle.detached;

            this.el = null;
            this.owner = null;
            this.scope = null;
            this.children = null;
            this.lifeCycle = LifeCycle.disposed;

            if (this._ondisposed) {
                this._ondisposed();
            }
        }
    }
    else if (this.lifeCycle.attached) {
        removeEl(this.el);
        this.lifeCycle = LifeCycle.detached;
    }
};

/**
 * 视图更新
 *
 * @param {Array} changes 数据变化信息
 */
Element.prototype._update = function (changes) {
    var dataHotspot = this.aNode.hotspot.data;
    if (dataHotspot && changesIsInDataRef(changes, dataHotspot)) {

        // update s-bind
        var me = this;
        nodeSBindUpdate(
            this,
            this.aNode.directives.bind,
            changes,
            function (name, value) {
                if (name in me.aNode.hotspot.props) {
                    return;
                }

                getPropHandler(me.tagName, name)(me.el, value, name, me);
            }
        );

        // update prop
        var dynamicProps = this.aNode.hotspot.dynamicProps;
        for (var i = 0, l = dynamicProps.length; i < l; i++) {
            var prop = dynamicProps[i];
            var propName = prop.name;

            for (var j = 0, changeLen = changes.length; j < changeLen; j++) {
                var change = changes[j];

                if (!isDataChangeByElement(change, this, propName)
                    && (
                        changeExprCompare(change.expr, prop.expr, this.scope)
                        || prop.hintExpr && changeExprCompare(change.expr, prop.hintExpr, this.scope)
                    )
                ) {
                    prop.handler(this.el, evalExpr(prop.expr, this.scope, this.owner), propName, this, prop);
                    break;
                }
            }
        }

        // update content
        var htmlDirective = this.aNode.directives.html;
        if (htmlDirective) {
            var len = changes.length;
            while (len--) {
                if (changeExprCompare(changes[len].expr, htmlDirective.value, this.scope)) {
                    // #[begin] error
                    warnSetHTML(this.el);
                    // #[end]

                    this.el.innerHTML = evalExpr(htmlDirective.value, this.scope, this.owner);
                    break;
                }
            }
        }
        else {
            for (var i = 0, l = this.children.length; i < l; i++) {
                this.children[i]._update(changes);
            }
        }
    }
};

/**
 * 执行完成attached状态的行为
 */
Element.prototype._attached = elementOwnAttached;

exports = module.exports = Element;
<commit_msg>rm useless variable<commit_after>/**
 * Copyright (c) Baidu Inc. All rights reserved.
 *
 * This source code is licensed under the MIT license.
 * See LICENSE file in the project root for license information.
 *
 * @file 元素节点类
 */

var changeExprCompare = require('../runtime/change-expr-compare');
var changesIsInDataRef = require('../runtime/changes-is-in-data-ref');
var evalExpr = require('../runtime/eval-expr');
var insertBefore = require('../browser/insert-before');
var LifeCycle = require('./life-cycle');
var NodeType = require('./node-type');
var reverseElementChildren = require('./reverse-element-children');
var isDataChangeByElement = require('./is-data-change-by-element');
var getPropHandler = require('./get-prop-handler');
var createNode = require('./create-node');
var elementOwnDetach = require('./element-own-detach');
var elementOwnDispose = require('./element-own-dispose');
var elementOwnOnEl = require('./element-own-on-el');
var elementOwnAttached = require('./element-own-attached');
var nodeSBindInit = require('./node-s-bind-init');
var nodeSBindUpdate = require('./node-s-bind-update');
var warnSetHTML = require('./warn-set-html');
var getNodePath = require('./get-node-path');

/**
 * 元素节点类
 *
 * @class
 * @param {Object} aNode 抽象节点
 * @param {Node} parent 父亲节点
 * @param {Model} scope 所属数据环境
 * @param {Component} owner 所属组件环境
 * @param {DOMChildrenWalker?} reverseWalker 子元素遍历对象
 */
function Element(aNode, parent, scope, owner, reverseWalker) {
    this.aNode = aNode;
    this.owner = owner;
    this.scope = scope;
    this.parent = parent;

    this.lifeCycle = LifeCycle.start;
    this.children = [];
    this._elFns = [];
    this.parentComponent = parent.nodeType === NodeType.CMPT
        ? parent
        : parent.parentComponent;

    this.tagName = aNode.tagName;

    // #[begin] allua
    // ie8- 不支持innerHTML输出自定义标签
    /* istanbul ignore if */
    if (ieOldThan9 && this.tagName.indexOf('-') > 0) {
        this.tagName = 'div';
    }
    // #[end]

    nodeSBindInit(this, aNode.directives.bind);
    this.lifeCycle = LifeCycle.inited;

    // #[begin] reverse
    if (reverseWalker) {
        var currentNode = reverseWalker.current;

        /* istanbul ignore if */
        if (!currentNode) {
            throw new Error('[SAN REVERSE ERROR] Element not found. \nPaths: '
                + getNodePath(this).join(' > '));
        }

        /* istanbul ignore if */
        if (currentNode.nodeType !== 1) {
            throw new Error('[SAN REVERSE ERROR] Element type not match, expect 1 but '
                + currentNode.nodeType + '.\nPaths: '
                + getNodePath(this).join(' > '));
        }

        /* istanbul ignore if */
        if (currentNode.tagName.toLowerCase() !== this.tagName) {
            throw new Error('[SAN REVERSE ERROR] Element tagName not match, expect '
                + this.tagName + ' but meat ' + currentNode.tagName.toLowerCase() + '.\nPaths: '
                + getNodePath(this).join(' > '));
        }

        this.el = currentNode;
        reverseWalker.goNext();

        reverseElementChildren(this, this.scope, this.owner);

        this.lifeCycle = LifeCycle.created;
        this._attached();
        this.lifeCycle = LifeCycle.attached;
    }
    // #[end]
}

Element.prototype.nodeType = NodeType.ELEM;

/**
 * 将元素attach到页面
 *
 * @param {HTMLElement} parentEl 要添加到的父元素
 * @param {HTMLElement＝} beforeEl 要添加到哪个元素之前
 */
Element.prototype.attach = function (parentEl, beforeEl) {
    if (!this.lifeCycle.attached) {
        if (!this.el) {
            var sourceNode = this.aNode.hotspot.sourceNode;
            var props = this.aNode.props;

            if (sourceNode) {
                this.el = sourceNode.cloneNode(false);
                props = this.aNode.hotspot.dynamicProps;
            }
            else {
                this.el = createEl(this.tagName);
            }

            if (this._sbindData) {
                for (var key in this._sbindData) {
                    if (this._sbindData.hasOwnProperty(key)) {
                        getPropHandler(this.tagName, key)(
                            this.el,
                            this._sbindData[key],
                            key,
                            this
                        );
                    }
                }
            }

            for (var i = 0, l = props.length; i < l; i++) {
                var prop = props[i];
                var propName = prop.name;
                var value = evalExpr(prop.expr, this.scope, this.owner);

                if (value || !emptyPropWhenCreate[propName]) {
                    prop.handler(this.el, value, propName, this, prop);
                }
            }

            this.lifeCycle = LifeCycle.created;
        }
        insertBefore(this.el, parentEl, beforeEl);

        if (!this._contentReady) {
            var htmlDirective = this.aNode.directives.html;

            if (htmlDirective) {
                // #[begin] error
                warnSetHTML(this.el);
                // #[end]

                this.el.innerHTML = evalExpr(htmlDirective.value, this.scope, this.owner);
            }
            else {
                for (var i = 0, l = this.aNode.children.length; i < l; i++) {
                    var childANode = this.aNode.children[i];
                    var child = childANode.Clazz
                        ? new childANode.Clazz(childANode, this, this.scope, this.owner)
                        : createNode(childANode, this, this.scope, this.owner);
                    this.children.push(child);
                    child.attach(this.el);
                }
            }

            this._contentReady = 1;
        }

        this._attached();

        this.lifeCycle = LifeCycle.attached;
    }
};

Element.prototype.detach = elementOwnDetach;
Element.prototype.dispose = elementOwnDispose;
Element.prototype._onEl = elementOwnOnEl;
Element.prototype._leave = function () {
    if (this.leaveDispose) {
        if (!this.lifeCycle.disposed) {
            var len = this.children.length;
            while (len--) {
                this.children[len].dispose(1, 1);
            }

            len = this._elFns.length;
            while (len--) {
                var fn = this._elFns[len];
                un(this.el, fn[0], fn[1], fn[2]);
            }
            this._elFns = null;

            // #[begin] allua
            /* istanbul ignore if */
            if (this._inputTimer) {
                clearInterval(this._inputTimer);
                this._inputTimer = null;
            }
            // #[end]

            // 如果没有parent，说明是一个root component，一定要从dom树中remove
            if (!this.disposeNoDetach || !this.parent) {
                removeEl(this.el);
            }

            this.lifeCycle = LifeCycle.detached;

            this.el = null;
            this.owner = null;
            this.scope = null;
            this.children = null;
            this.lifeCycle = LifeCycle.disposed;

            if (this._ondisposed) {
                this._ondisposed();
            }
        }
    }
    else if (this.lifeCycle.attached) {
        removeEl(this.el);
        this.lifeCycle = LifeCycle.detached;
    }
};

/**
 * 视图更新
 *
 * @param {Array} changes 数据变化信息
 */
Element.prototype._update = function (changes) {
    var dataHotspot = this.aNode.hotspot.data;
    if (dataHotspot && changesIsInDataRef(changes, dataHotspot)) {

        // update s-bind
        var me = this;
        nodeSBindUpdate(
            this,
            this.aNode.directives.bind,
            changes,
            function (name, value) {
                if (name in me.aNode.hotspot.props) {
                    return;
                }

                getPropHandler(me.tagName, name)(me.el, value, name, me);
            }
        );

        // update prop
        var dynamicProps = this.aNode.hotspot.dynamicProps;
        for (var i = 0, l = dynamicProps.length; i < l; i++) {
            var prop = dynamicProps[i];
            var propName = prop.name;

            for (var j = 0, changeLen = changes.length; j < changeLen; j++) {
                var change = changes[j];

                if (!isDataChangeByElement(change, this, propName)
                    && (
                        changeExprCompare(change.expr, prop.expr, this.scope)
                        || prop.hintExpr && changeExprCompare(change.expr, prop.hintExpr, this.scope)
                    )
                ) {
                    prop.handler(this.el, evalExpr(prop.expr, this.scope, this.owner), propName, this, prop);
                    break;
                }
            }
        }

        // update content
        var htmlDirective = this.aNode.directives.html;
        if (htmlDirective) {
            var len = changes.length;
            while (len--) {
                if (changeExprCompare(changes[len].expr, htmlDirective.value, this.scope)) {
                    // #[begin] error
                    warnSetHTML(this.el);
                    // #[end]

                    this.el.innerHTML = evalExpr(htmlDirective.value, this.scope, this.owner);
                    break;
                }
            }
        }
        else {
            for (var i = 0, l = this.children.length; i < l; i++) {
                this.children[i]._update(changes);
            }
        }
    }
};

/**
 * 执行完成attached状态的行为
 */
Element.prototype._attached = elementOwnAttached;

exports = module.exports = Element;
<|endoftext|>


<commit_before>    /* global VPTree, exports */

	/*───────────────────────────────────────────────────────────────────────────┐
	 │   Selection/partition algorithm                                           │
	 └───────────────────────────────────────────────────────────────────────────*/

	function partition(list, left, right, pivotIndex, comp) {
		var pivotValue = list[pivotIndex];
		var swap = list[pivotIndex];	// Move pivot to end
		list[pivotIndex] = list[right];
		list[right] = swap;
		var storeIndex = left;
		for (var i = left; i < right; i++) {
			if (comp(list[i], pivotValue)) {
				swap = list[storeIndex];
				list[storeIndex] = list[i];
				list[i] = swap;
				storeIndex++;
			}
		}
		swap = list[right];				// Move pivot to its final place
		list[right] = list[storeIndex];
		list[storeIndex] = swap;
		return storeIndex;
	}

	// Pivot selection : computes the median of elements a, b and c of the list,
	// according to comparator comp.
	function medianOf3(list, a, b, c, comp) {
		var A = list[a], B = list[b], C = list[c];
		return comp (A, B) ?
            comp (B, C) ? b : comp (A, C) ? c : a :
			comp (A, C) ? a : comp (B, C) ? c : b;
	}

	/**
	 * Quickselect : Finds the nth smallest number in a list according to comparator comp.
	 * All elements smaller than the nth element are moved to its left (in no particular order),
	 * and all elements greater thant the nth are moved to its right.
	 *
	 * The funny mix of 0-based and 1-based indexes comes from the C++
	 * Standard Library function nth_element.
	 *
	 * @param {Array} list - the list to partition
	 * @param {int} left - index in the list of the first element of the sublist.
	 * @param {int} right - index in the list of the last element of the sublist (inclusive)
	 * @param {int} nth - index, in the range [1, sublist.length] of the element to find.
	 * @param {function} comp - a comparator, i.e. a boolean function accepting two parameters a and b,
	 *        and returning true if a < b and false if a >= b.
	 *
	 * See http://en.wikipedia.org/wiki/Quickselect
	 * And /include/bits/stl_algo.h in the GCC Standard Library ( http://gcc.gnu.org/libstdc++/ )
	 */
	function nth_element(list, left, nth, right, comp) {
		if (nth <= 0 || nth > (right-left+1)) throw new Error("VPTree.nth_element: nth must be in range [1, right-left+1] (nth="+nth+")");
		var pivotIndex, pivotNewIndex, pivotDist;
		for (;;) {
			// select pivotIndex between left and right
			pivotIndex = medianOf3(list, left, right, (left + right) >> 1, comp);
			pivotNewIndex = partition(list, left, right, pivotIndex, comp);
			pivotDist = pivotNewIndex - left + 1;
			if (pivotDist === nth) {
				return list[pivotNewIndex];
			}
			else if (nth < pivotDist) {
				right = pivotNewIndex - 1;
			}
			else {
				nth -= pivotDist;
				left = pivotNewIndex + 1;
			}
		}
	}

	/**
	 * Wrapper around nth_element with a 0-based index.
	 */
	function select(list, k, comp) {
		if (k < 0 || k >= list.length) {
            throw new Error("VPTree.select: k must be in range [0, list.length-1] (k="+k+")");
        }
		return nth_element(list, 0, k+1, list.length-1, comp);
	}

	/*───────────────────────────────────────────────────────────────────────────┐
	 │   vp-tree creation                                                        │
	 └───────────────────────────────────────────────────────────────────────────*/
	/** Selects a vantage point in a set.
	 *  We trivially pick one at random.
	 *  TODO this could be improved by random sampling to maximize spread.
	 */
	function selectVPIndex(list) {
		return Math.floor(Math.random() * list.length);
	}

	var distanceComparator = function(a, b) { return a.dist < b.dist; };

	/**
	 * Builds and returns a vp-tree from the list S.
	 * @param {Array} S array of objects to structure into a vp-tree.
	 * @param {function} distance a function returning the distance between 2 ojects from the list S.
	 * @param {number} nb (maximum) bucket size. 0 or undefined = no buckets used.
	 * @return {object} vp-tree.
	 */
	function buildVPTree(S, distance, nb) {
		var list = [];
		for (var i = 0, n = S.length; i < n; i++) {
			list[i] = {
				i: i
				//hist: []		// unused (yet)
			};
		}

		var tree = recurseVPTree(S, list, distance, nb);
		return new VPTree(S, distance, tree);
	}

	function recurseVPTree(S, list, distance, nb) {
		if (list.length === 0) return null;
		var i;

		// Is this a leaf node ?
		var listLength = list.length;
		if (nb > 0 && listLength <= nb) {
			var bucket = [];
			for (i = 0; i < listLength; i++) {
				bucket[i] = list[i].i;
			}
			return bucket;
		}

		// Non-leaf node.
		// Constructs a node with the selected vantage point extracted from the set.
		var vpIndex = selectVPIndex(list),
			node = list[vpIndex];
		list.splice(vpIndex, 1);
		listLength--;
		// We can't node.dist yet, so don't show it in the vp-tree output.
        node = { i: node.i };
		if (listLength === 0) return node;

		// Adds to each item its distance to the vantage point.
		// This ensures each distance is computed only once.
		var vp = S[node.i],
			dmin = Infinity,
			dmax = 0,
			item, dist, n;
		for (i = 0, n = listLength; i < n; i++) {
			item = list[i];
			dist = distance(vp, S[item.i]);
			item.dist = dist;
			//item.hist.push(dist);	// unused (yet)
			if (dmin > dist) dmin = dist;
			if (dmax < dist) dmax = dist;
		}
		node.m = dmin;
		node.M = dmax;

		// Partitions the set around the median distance.
		var medianIndex = listLength >> 1,
			median = select(list, medianIndex, distanceComparator);

		// Recursively builds vp-trees with the 2 resulting subsets.
		var leftItems = list.splice(0, medianIndex),
			rightItems = list;
		node.μ = median.dist;
		node.L = recurseVPTree(S, leftItems, distance, nb);
		node.R = recurseVPTree(S, rightItems, distance, nb);
		return node;
	}

	/** Stringifies a vp-tree data structure.
	 *  JSON without the null nodes and the quotes around object keys, to save space.
	 */
	function stringify(root) {
		var stack = [root || this.tree], s = '';
		while (stack.length) {
			var node = stack.pop();

			// Happens if the bucket size is greater thant the dataset.
			if (node.length) return '['+node.join(',')+']';

			s += '{i:' + node.i;
			if (node.hasOwnProperty('m')) {
				s += ',m:' + node.m + ',M:' + node.M + ',μ:' + node.μ;
			}
			if (node.hasOwnProperty('b')) {
				s += ',b:[' + node.b + ']';
			}
			if (node.hasOwnProperty('L')) {
				var L = node.L;
				if (L) {
					s += ',L:';
					if (L.length) s += '[' + L + ']';
					else s += stringify(L);
				}
			}
			if (node.hasOwnProperty('R')) {
				var R = node.R;
				if (R) {
					s += ',R:';
					if (R.length) s += '[' + R + ']';
					else s += stringify(R);
				}
			}
			s += '}';
		}
		return s;
	}

	/*───────────────────────────────────────────────────────────────────────────┐
	 │   Build Public API                                                        │
	 └───────────────────────────────────────────────────────────────────────────*/

	exports.select = select;
	exports.build = buildVPTree;

<commit_msg>Fix identation<commit_after>    /* global VPTree, exports */

	/*───────────────────────────────────────────────────────────────────────────┐
	 │   Selection/partition algorithm                                           │
	 └───────────────────────────────────────────────────────────────────────────*/

	function partition(list, left, right, pivotIndex, comp) {
		var pivotValue = list[pivotIndex];
		var swap = list[pivotIndex];	// Move pivot to end
		list[pivotIndex] = list[right];
		list[right] = swap;
		var storeIndex = left;
		for (var i = left; i < right; i++) {
			if (comp(list[i], pivotValue)) {
				swap = list[storeIndex];
				list[storeIndex] = list[i];
				list[i] = swap;
				storeIndex++;
			}
		}
		swap = list[right];				// Move pivot to its final place
		list[right] = list[storeIndex];
		list[storeIndex] = swap;
		return storeIndex;
	}

	// Pivot selection : computes the median of elements a, b and c of the list,
	// according to comparator comp.
	function medianOf3(list, a, b, c, comp) {
		var A = list[a], B = list[b], C = list[c];
		return comp (A, B) ?
            comp (B, C) ? b : comp (A, C) ? c : a :
			comp (A, C) ? a : comp (B, C) ? c : b;
	}

	/**
	 * Quickselect : Finds the nth smallest number in a list according to comparator comp.
	 * All elements smaller than the nth element are moved to its left (in no particular order),
	 * and all elements greater thant the nth are moved to its right.
	 *
	 * The funny mix of 0-based and 1-based indexes comes from the C++
	 * Standard Library function nth_element.
	 *
	 * @param {Array} list - the list to partition
	 * @param {int} left - index in the list of the first element of the sublist.
	 * @param {int} right - index in the list of the last element of the sublist (inclusive)
	 * @param {int} nth - index, in the range [1, sublist.length] of the element to find.
	 * @param {function} comp - a comparator, i.e. a boolean function accepting two parameters a and b,
	 *        and returning true if a < b and false if a >= b.
	 *
	 * See http://en.wikipedia.org/wiki/Quickselect
	 * And /include/bits/stl_algo.h in the GCC Standard Library ( http://gcc.gnu.org/libstdc++/ )
	 */
	function nth_element(list, left, nth, right, comp) {
		if (nth <= 0 || nth > (right-left+1)) throw new Error("VPTree.nth_element: nth must be in range [1, right-left+1] (nth="+nth+")");
		var pivotIndex, pivotNewIndex, pivotDist;
		for (;;) {
			// select pivotIndex between left and right
			pivotIndex = medianOf3(list, left, right, (left + right) >> 1, comp);
			pivotNewIndex = partition(list, left, right, pivotIndex, comp);
			pivotDist = pivotNewIndex - left + 1;
			if (pivotDist === nth) {
				return list[pivotNewIndex];
			}
			else if (nth < pivotDist) {
				right = pivotNewIndex - 1;
			}
			else {
				nth -= pivotDist;
				left = pivotNewIndex + 1;
			}
		}
	}

	/**
	 * Wrapper around nth_element with a 0-based index.
	 */
	function select(list, k, comp) {
		if (k < 0 || k >= list.length) {
            throw new Error("VPTree.select: k must be in range [0, list.length-1] (k="+k+")");
        }
		return nth_element(list, 0, k+1, list.length-1, comp);
	}

	/*───────────────────────────────────────────────────────────────────────────┐
	 │   vp-tree creation                                                        │
	 └───────────────────────────────────────────────────────────────────────────*/
	/** Selects a vantage point in a set.
	 *  We trivially pick one at random.
	 *  TODO this could be improved by random sampling to maximize spread.
	 */
	function selectVPIndex(list) {
		return Math.floor(Math.random() * list.length);
	}

	var distanceComparator = function(a, b) { return a.dist < b.dist; };

	/**
	 * Builds and returns a vp-tree from the list S.
	 * @param {Array} S array of objects to structure into a vp-tree.
	 * @param {function} distance a function returning the distance between 2 ojects from the list S.
	 * @param {number} nb (maximum) bucket size. 0 or undefined = no buckets used.
	 * @return {object} vp-tree.
	 */
	function buildVPTree(S, distance, nb) {
		var list = [];
		for (var i = 0, n = S.length; i < n; i++) {
			list[i] = {
				i: i
				//hist: []		// unused (yet)
			};
		}

		var tree = recurseVPTree(S, list, distance, nb);
		return new VPTree(S, distance, tree);
	}

	function recurseVPTree(S, list, distance, nb) {
		if (list.length === 0) return null;
		var i;

		// Is this a leaf node ?
		var listLength = list.length;
		if (nb > 0 && listLength <= nb) {
			var bucket = [];
			for (i = 0; i < listLength; i++) {
				bucket[i] = list[i].i;
			}
			return bucket;
		}

		// Non-leaf node.
		// Constructs a node with the selected vantage point extracted from the set.
		var vpIndex = selectVPIndex(list),
			node = list[vpIndex];
		list.splice(vpIndex, 1);
		listLength--;
		// We can't node.dist yet, so don't show it in the vp-tree output.
		node = { i: node.i };
		if (listLength === 0) return node;

		// Adds to each item its distance to the vantage point.
		// This ensures each distance is computed only once.
		var vp = S[node.i],
			dmin = Infinity,
			dmax = 0,
			item, dist, n;
		for (i = 0, n = listLength; i < n; i++) {
			item = list[i];
			dist = distance(vp, S[item.i]);
			item.dist = dist;
			//item.hist.push(dist);	// unused (yet)
			if (dmin > dist) dmin = dist;
			if (dmax < dist) dmax = dist;
		}
		node.m = dmin;
		node.M = dmax;

		// Partitions the set around the median distance.
		var medianIndex = listLength >> 1,
			median = select(list, medianIndex, distanceComparator);

		// Recursively builds vp-trees with the 2 resulting subsets.
		var leftItems = list.splice(0, medianIndex),
			rightItems = list;
		node.μ = median.dist;
		node.L = recurseVPTree(S, leftItems, distance, nb);
		node.R = recurseVPTree(S, rightItems, distance, nb);
		return node;
	}

	/** Stringifies a vp-tree data structure.
	 *  JSON without the null nodes and the quotes around object keys, to save space.
	 */
	function stringify(root) {
		var stack = [root || this.tree], s = '';
		while (stack.length) {
			var node = stack.pop();

			// Happens if the bucket size is greater thant the dataset.
			if (node.length) return '['+node.join(',')+']';

			s += '{i:' + node.i;
			if (node.hasOwnProperty('m')) {
				s += ',m:' + node.m + ',M:' + node.M + ',μ:' + node.μ;
			}
			if (node.hasOwnProperty('b')) {
				s += ',b:[' + node.b + ']';
			}
			if (node.hasOwnProperty('L')) {
				var L = node.L;
				if (L) {
					s += ',L:';
					if (L.length) s += '[' + L + ']';
					else s += stringify(L);
				}
			}
			if (node.hasOwnProperty('R')) {
				var R = node.R;
				if (R) {
					s += ',R:';
					if (R.length) s += '[' + R + ']';
					else s += stringify(R);
				}
			}
			s += '}';
		}
		return s;
	}

	/*───────────────────────────────────────────────────────────────────────────┐
	 │   Build Public API                                                        │
	 └───────────────────────────────────────────────────────────────────────────*/

	exports.select = select;
	exports.build = buildVPTree;

<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import wrapDisplayName from 'recompose/wrapDisplayName'
import jwtDecode from 'jwt-decode'

export default (propName, propsMapping) => Target => {
  class WithJwtProps extends Component {
    constructor(props, context) {
      super(props, context)

      this.state = this.buildState(props[propName])
    }

    componentWillReceiveProps(nextProps) {
      if (nextProps[propName] !== this.props[propName]) {
        this.setState(this.buildState(nextProps[propName]))
      }
    }

    buildState(token) {
      try {
        const decodedToken = jwtDecode(token)

        if (!propsMapping) {
          return decodedToken
        }

        return Object.keys(propsMapping).reduce((result, fromKey) => {
          const toKey = propsMapping[fromKey]
          result[toKey] = decodedToken[fromKey]

          return result
        }, {})
      } catch (e) {
        this.props.onJwtError(e)

        return {}
      }
    }

    render() {
      return <Target {...this.props} {...this.state} />
    }
  }

  WithJwtProps.defaultProps = {
    onJWTError: () => {},
  }

  WithJwtProps.propTypes = {
    onJwtError: PropTypes.func,
  }

  WithJwtProps.displayName = wrapDisplayName(Target, 'withJwtProps')

  return WithJwtProps
}
<commit_msg>Fix withJwtProps default props<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import wrapDisplayName from 'recompose/wrapDisplayName'
import jwtDecode from 'jwt-decode'

export default (propName, propsMapping) => Target => {
  class WithJwtProps extends Component {
    constructor(props, context) {
      super(props, context)

      this.state = this.buildState(props[propName])
    }

    componentWillReceiveProps(nextProps) {
      if (nextProps[propName] !== this.props[propName]) {
        this.setState(this.buildState(nextProps[propName]))
      }
    }

    buildState(token) {
      try {
        const decodedToken = jwtDecode(token)

        if (!propsMapping) {
          return decodedToken
        }

        return Object.keys(propsMapping).reduce((result, fromKey) => {
          const toKey = propsMapping[fromKey]
          result[toKey] = decodedToken[fromKey]

          return result
        }, {})
      } catch (e) {
        this.props.onJwtError(e)

        return {}
      }
    }

    render() {
      return <Target {...this.props} {...this.state} />
    }
  }

  WithJwtProps.defaultProps = {
    onJwtError: () => {},
  }

  WithJwtProps.propTypes = {
    onJwtError: PropTypes.func,
  }

  WithJwtProps.displayName = wrapDisplayName(Target, 'withJwtProps')

  return WithJwtProps
}
<|endoftext|>


<commit_before>/**
 * brightwheel
 *
 * Copyright © 2016 Allen Smith <loranallensmith@github.com>. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

 /** @jsx etch.dom */

import etch from 'etch';
import classNames from 'classnames';
import BrightwheelComponent from './brightwheel-component';

class Input extends BrightwheelComponent {

  render() {
    let classes = classNames(
      'form-control',
      this.properties.classNames
    );

    return (
      <input type={this.properties.type} name={this.properties.name} {...this.properties.attributes} className={classes} placeholder={this.properties.placeholder}></input>
    );
  }

}

export default Input;
<commit_msg>Move attribute spread for input<commit_after>/**
 * brightwheel
 *
 * Copyright © 2016 Allen Smith <loranallensmith@github.com>. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

 /** @jsx etch.dom */

import etch from 'etch';
import classNames from 'classnames';
import BrightwheelComponent from './brightwheel-component';

class Input extends BrightwheelComponent {

  render() {
    let classes = classNames(
      'form-control',
      this.properties.classNames
    );

    return (
      <input {...this.properties.attributes}  type={this.properties.type} name={this.properties.name} className={classes} placeholder={this.properties.placeholder}></input>
    );
  }

}

export default Input;
<|endoftext|>


<commit_before>var Input = module.exports = function() {
	var exports = {};

	var pointerLocked = false;
	var mouseState = [], currentlyPressedKeys = [];	// probably shouldn't use arrays lots of empty space
	var downKeys = [], upKeys = []; // Keys pressed or released this frame
	var canvas;
	var init = exports.init = function(targetCanvas) {
			canvas = targetCanvas;
			canvas.addEventListener("mousemove", handleMouseMove);
			canvas.addEventListener("mousedown", handleMouseDown, true);
			canvas.addEventListener("mouseup", handleMouseUp);
			canvas.addEventListener('mousemove', (event) => {
				MouseDelta[0] += event.movementX;
				MouseDelta[1] += event.movementY;
			});
			document.addEventListener('pointerlockchange', (event) => {
				pointerLocked = !!(document.pointerLockElement || document.mozPointerLockElement); // polyfill
			});
			canvas.requestPointerLock = canvas.requestPointerLock || canvas.mozRequestPointerLock; // polyfill

			window.addEventListener("keyup", handleKeyUp);
			window.addEventListener("keydown", handleKeyDown);
			window.addEventListener("blur", handleBlur);
	};

	exports.isPointerLocked = function() {
		return pointerLocked;
	};

	exports.requestPointerLock = function() {
		canvas.requestPointerLock();
	};

	var MouseDelta = exports.MouseDelta = [0, 0];
	var MousePosition = exports.MousePosition = [0, 0];

	// TODO: Add signalEndFrame and store keyDown [] and keyUp[] array for
	// querying as well, although the option of just subscribing to the events
	// in game code is also there but need to use DescriptionToKeyCode

	var keyPressed = function(key) {
		if (!isNaN(key) && !key.length) {
			return currentlyPressedKeys[key];
		}
		else if (key) {
			var map = DescriptionToKeyCode[key];
			return (map) ? !!currentlyPressedKeys[map] : false;
		}
		else {
			return false;
		}
	};

	var keyUp = exports.keyUp = function(key) {
		if (!isNaN(key) && !key.length) {
			return upKeys[key];
		}
		else if (key) {
			var map = DescriptionToKeyCode[key];
			return (map) ? !!upKeys[map] : false;
		}
		else {
			return false;
		}
	};

	var keyDown = exports.keyDown = function(key, thisFrame) {
		if (!thisFrame) {
			return keyPressed(key);
		} else {
			if (!isNaN(key) && !key.length) {
				return downKeys[key];
			}
			else if (key) {
				var map = DescriptionToKeyCode[key];
				return (map) ? !!downKeys[map] : false;
			}
			else {
				return false;
			}
		}
	};

	var mouseDown = exports.mouseDown = function(button) {
		if (!isNaN(button) && !button.length) {
			return mouseState[button];
		}
		else if (button) {
			var map = DescriptionToMouseButton[button];
			return (!isNaN(map)) ? mouseState[map] : false;
		}
		else {
			return false;
		}
	};

	exports.handleFrameFinished = function() {
		MouseDelta[0] = 0;
		MouseDelta[1] = 0;
		downKeys.length = 0;
		upKeys.length = 0;
	};

	var handleKeyDown = function(event) {
		downKeys[event.keyCode] = true;
		currentlyPressedKeys[event.keyCode] = true;
	};

	var handleKeyUp = function(event) {
		currentlyPressedKeys[event.keyCode] = false;
		upKeys[event.keyCode] = true;
	};

	var handleBlur = function(event) {
		downKeys.length = 0;
		currentlyPressedKeys.length = 0;
		upKeys.length = 0;	// Q: Should we be copying currently pressed Keys as they've kinda been released?
	};

	var handleMouseMove = function(event) {
		MousePosition[0] = event.pageX;
		MousePosition[1] = event.pageY;
	};

	var handleMouseDown = function(event) {
		mouseState[event.button] = true;
		return false;
	};

	var handleMouseUp = function(event) {
		mouseState[event.button] = false;
	};

	// TODO: Add Numpad Keys
	// TODO: Deal with shift in map (probably going to need to move to a function from JSON object for this)
	var DescriptionToKeyCode = exports.DescriptionToKeyCode = {
		"a": 65,
		"b": 66,
		"c": 67,
		"d": 68,
		"e": 69,
		"f": 70,
		"g": 71,
		"h": 72,
		"i": 73,
		"j": 74,
		"k": 75,
		"l": 76,
		"m": 77,
		"n": 78,
		"o": 79,
		"p": 80,
		"q": 81,
		"r": 82,
		"s": 83,
		"t": 84,
		"u": 85,
		"v": 86,
		"w": 87,
		"x": 88,
		"y": 89,
		"z": 90,
		"Backspace": 8,
		"Tab": 9,
		"Enter": 13,
		"Shift": 16,
		"Ctrl": 17,
		"Alt": 18,
		"PauseBreak": 19,
		"Caps": 20,
		"Esc": 27,
		"Space": 32,
		"PageUp": 33,
		"PageDown": 34,
		"End": 35,
		"Home": 36,
		"Left": 37,
		"Up": 38,
		"Right": 39,
		"Down": 40,
		"Insert": 45,
		"Delete": 46,
		"0": 48,
		"1": 49,
		"2": 50,
		"3": 51,
		"4": 52,
		"5": 53,
		"6": 54,
		"7": 55,
		"8": 56,
		"9": 57,
		";": 59,
		"=": 61,
		"-": 189,
		",": 188,
		".": 190,
		"/": 191,
		"|": 220,
		"[": 219,
		"]": 221,
		"`": 223,
		"'": 192,
		"#": 222
	};

	var KeyCodeToDescription = exports.KeyCodeToDescription = {
		65: "a",
		66: "b",
		67: "c",
		68: "d",
		69: "e",
		70: "f",
		71: "g",
		72: "h",
		73: "i",
		74: "j",
		75: "k",
		76: "l",
		77: "m",
		78: "n",
		79: "o",
		80: "p",
		81: "q",
		82: "r",
		83: "s",
		84: "t",
		85: "u",
		86: "v",
		87: "w",
		88: "x",
		89: "y",
		90: "z",
		8: "Backspace",
		9: "Tab",
		13: "Enter",
		16: "Shift",
		17: "Ctrl",
		18: "Alt",
		19: "PauseBreak",
		20: "Caps",
		27: "Esc",
		32: "Space",
		33: "PageUp",
		34: "PageDown",
		35: "End",
		36: "Home",
		37: "Left",
		38: "Up",
		39: "Right",
		40: "Down",
		45: "Insert",
		46: "Delete",
		48: "0",
		49: "1",
		50: "2",
		51: "3",
		52: "4",
		53: "5",
		54: "6",
		55: "7",
		56: "8",
		57: "9",
		59: ";",
		61: "=",
		189: "-",
		188: ",",
		190: ".",
		191: "/",
		220: "|",
		219: "[",
		221: "]",
		223: "`",
		192: "'",
		222: "#"
	};

	var MouseButtonToDescription = exports.MouseButtonToDescription = {
		0: "LeftMouseButton",
		1: "MiddleMouseButton",
		2: "RightMouseButton"
	};

	var DescriptionToMouseButton = exports.DescriptionToMouseButton = {
		"LeftMouseButton": 0,
		"MiddleMouseButton": 1,
		"RightMouseButton": 2
	};

	return exports;
}();
<commit_msg>Fix for keydown being called repeatedly<commit_after>var Input = module.exports = function() {
	var exports = {};

	var pointerLocked = false;
	var mouseState = [], currentlyPressedKeys = [];	// probably shouldn't use arrays lots of empty space
	var downKeys = [], upKeys = []; // Keys pressed or released this frame
	var canvas;
	var init = exports.init = function(targetCanvas) {
			canvas = targetCanvas;
			canvas.addEventListener("mousemove", handleMouseMove);
			canvas.addEventListener("mousedown", handleMouseDown, true);
			canvas.addEventListener("mouseup", handleMouseUp);
			canvas.addEventListener('mousemove', (event) => {
				MouseDelta[0] += event.movementX;
				MouseDelta[1] += event.movementY;
			});
			document.addEventListener('pointerlockchange', (event) => {
				pointerLocked = !!(document.pointerLockElement || document.mozPointerLockElement); // polyfill
			});
			canvas.requestPointerLock = canvas.requestPointerLock || canvas.mozRequestPointerLock; // polyfill

			window.addEventListener("keyup", handleKeyUp);
			window.addEventListener("keydown", handleKeyDown);
			window.addEventListener("blur", handleBlur);
	};

	exports.isPointerLocked = function() {
		return pointerLocked;
	};

	exports.requestPointerLock = function() {
		canvas.requestPointerLock();
	};

	var MouseDelta = exports.MouseDelta = [0, 0];
	var MousePosition = exports.MousePosition = [0, 0];

	// TODO: Add signalEndFrame and store keyDown [] and keyUp[] array for
	// querying as well, although the option of just subscribing to the events
	// in game code is also there but need to use DescriptionToKeyCode

	var keyPressed = function(key) {
		if (!isNaN(key) && !key.length) {
			return currentlyPressedKeys[key];
		}
		else if (key) {
			var map = DescriptionToKeyCode[key];
			return (map) ? !!currentlyPressedKeys[map] : false;
		}
		else {
			return false;
		}
	};

	var keyUp = exports.keyUp = function(key) {
		if (!isNaN(key) && !key.length) {
			return upKeys[key];
		}
		else if (key) {
			var map = DescriptionToKeyCode[key];
			return (map) ? !!upKeys[map] : false;
		}
		else {
			return false;
		}
	};

	var keyDown = exports.keyDown = function(key, thisFrame) {
		if (!thisFrame) {
			return keyPressed(key);
		} else {
			if (!isNaN(key) && !key.length) {
				return downKeys[key];
			}
			else if (key) {
				var map = DescriptionToKeyCode[key];
				return (map) ? !!downKeys[map] : false;
			}
			else {
				return false;
			}
		}
	};

	var mouseDown = exports.mouseDown = function(button) {
		if (!isNaN(button) && !button.length) {
			return mouseState[button];
		}
		else if (button) {
			var map = DescriptionToMouseButton[button];
			return (!isNaN(map)) ? mouseState[map] : false;
		}
		else {
			return false;
		}
	};

	exports.handleFrameFinished = function() {
		MouseDelta[0] = 0;
		MouseDelta[1] = 0;
		downKeys.length = 0;
		upKeys.length = 0;
	};

	var handleKeyDown = function(event) {
		// keyDown event can get called multiple times after a short delay
		if (!currentlyPressedKeys[event.keyCode]) {
			downKeys[event.keyCode] = true;
		}
		currentlyPressedKeys[event.keyCode] = true;
	};

	var handleKeyUp = function(event) {
		currentlyPressedKeys[event.keyCode] = false;
		upKeys[event.keyCode] = true;
	};

	var handleBlur = function(event) {
		downKeys.length = 0;
		currentlyPressedKeys.length = 0;
		upKeys.length = 0;	// Q: Should we be copying currently pressed Keys as they've kinda been released?
	};

	var handleMouseMove = function(event) {
		MousePosition[0] = event.pageX;
		MousePosition[1] = event.pageY;
	};

	var handleMouseDown = function(event) {
		mouseState[event.button] = true;
		return false;
	};

	var handleMouseUp = function(event) {
		mouseState[event.button] = false;
	};

	// TODO: Add Numpad Keys
	// TODO: Deal with shift in map (probably going to need to move to a function from JSON object for this)
	var DescriptionToKeyCode = exports.DescriptionToKeyCode = {
		"a": 65,
		"b": 66,
		"c": 67,
		"d": 68,
		"e": 69,
		"f": 70,
		"g": 71,
		"h": 72,
		"i": 73,
		"j": 74,
		"k": 75,
		"l": 76,
		"m": 77,
		"n": 78,
		"o": 79,
		"p": 80,
		"q": 81,
		"r": 82,
		"s": 83,
		"t": 84,
		"u": 85,
		"v": 86,
		"w": 87,
		"x": 88,
		"y": 89,
		"z": 90,
		"Backspace": 8,
		"Tab": 9,
		"Enter": 13,
		"Shift": 16,
		"Ctrl": 17,
		"Alt": 18,
		"PauseBreak": 19,
		"Caps": 20,
		"Esc": 27,
		"Space": 32,
		"PageUp": 33,
		"PageDown": 34,
		"End": 35,
		"Home": 36,
		"Left": 37,
		"Up": 38,
		"Right": 39,
		"Down": 40,
		"Insert": 45,
		"Delete": 46,
		"0": 48,
		"1": 49,
		"2": 50,
		"3": 51,
		"4": 52,
		"5": 53,
		"6": 54,
		"7": 55,
		"8": 56,
		"9": 57,
		";": 59,
		"=": 61,
		"-": 189,
		",": 188,
		".": 190,
		"/": 191,
		"|": 220,
		"[": 219,
		"]": 221,
		"`": 223,
		"'": 192,
		"#": 222
	};

	var KeyCodeToDescription = exports.KeyCodeToDescription = {
		65: "a",
		66: "b",
		67: "c",
		68: "d",
		69: "e",
		70: "f",
		71: "g",
		72: "h",
		73: "i",
		74: "j",
		75: "k",
		76: "l",
		77: "m",
		78: "n",
		79: "o",
		80: "p",
		81: "q",
		82: "r",
		83: "s",
		84: "t",
		85: "u",
		86: "v",
		87: "w",
		88: "x",
		89: "y",
		90: "z",
		8: "Backspace",
		9: "Tab",
		13: "Enter",
		16: "Shift",
		17: "Ctrl",
		18: "Alt",
		19: "PauseBreak",
		20: "Caps",
		27: "Esc",
		32: "Space",
		33: "PageUp",
		34: "PageDown",
		35: "End",
		36: "Home",
		37: "Left",
		38: "Up",
		39: "Right",
		40: "Down",
		45: "Insert",
		46: "Delete",
		48: "0",
		49: "1",
		50: "2",
		51: "3",
		52: "4",
		53: "5",
		54: "6",
		55: "7",
		56: "8",
		57: "9",
		59: ";",
		61: "=",
		189: "-",
		188: ",",
		190: ".",
		191: "/",
		220: "|",
		219: "[",
		221: "]",
		223: "`",
		192: "'",
		222: "#"
	};

	var MouseButtonToDescription = exports.MouseButtonToDescription = {
		0: "LeftMouseButton",
		1: "MiddleMouseButton",
		2: "RightMouseButton"
	};

	var DescriptionToMouseButton = exports.DescriptionToMouseButton = {
		"LeftMouseButton": 0,
		"MiddleMouseButton": 1,
		"RightMouseButton": 2
	};

	return exports;
}();
<|endoftext|>