<commit_before>var _                 = require('_'),
    D                 = require('D'),
    nodeType          = require('nodeType'),
    isString          = require('is/string'),
    isAttached        = require('is/attached'),
    isElement         = require('is/element'),
    isWindow          = require('is/window'),
    isDocument        = require('is/document'),
    isD               = require('is/D'),
    order             = require('order'),
    unique            = require('./array/unique'),
    selectorFilter    = require('./selectors/filter'),
    Fizzle            = require('Fizzle');

var getSiblings = function(context) {
        var idx    = 0,
            len    = context.length,
            result = [];
        for (; idx < len; idx++) {
            var sibs = _getNodeSiblings(context[idx]);
            if (sibs.length) { result.push(sibs); }
        }
        return _.flatten(result);
    },

    _getNodeSiblings = function(node) {
        var parent = node.parentNode;

        if (!parent) {
            return [];
        }

        var sibs = _.toArray(parent.children),
            idx  = sibs.length;

        while (idx--) {
            // Exclude the node itself from the list of its parent's children
            if (sibs[idx] === node) {
                sibs.splice(idx, 1);
            }
        }

        return sibs;
    },

    // Children ------
    getChildren = (arr) => _.flatten(_.map(arr, _children)),
    _children = function(elem) {
        var kids = elem.children,
            idx  = 0, len  = kids.length,
            arr  = new Array(len);
        for (; idx < len; idx++) {
            arr[idx] = kids[idx];
        }
        return arr;
    },

    // Parents ------
    getClosest = function(elems, selector, context) {
        var idx = 0,
            len = elems.length,
            parents,
            closest,
            result = [];
        for (; idx < len; idx++) {
            parents = _crawlUpNode(elems[idx], context);
            parents.unshift(elems[idx]);
            closest = selectorFilter(parents, selector);
            if (closest.length) {
                result.push(closest[0]);
            }
        }
        return _.flatten(result);
    },

    getParents = function(context) {
        var idx = 0,
            len = context.length,
            parents,
            result = [];
        for (; idx < len; idx++) {
            parents = _crawlUpNode(context[idx]);
            result.push(parents);
        }
        return _.flatten(result);
    },

    getParentsUntil = function(d, stopSelector) {
        var idx = 0,
            len = d.length,
            parents,
            result = [];
        for (; idx < len; idx++) {
            parents = _crawlUpNode(d[idx], null, stopSelector);
            result.push(parents);
        }
        return _.flatten(result);
    },

    _crawlUpNode = function(node, context, stopSelector) {
        var result = [],
            parent = node;

        while ((parent   = getNodeParent(parent))    &&
               !nodeType.doc(parent)            &&
               (!context      || parent !== context) &&
               (!stopSelector || !Fizzle.is(stopSelector).match(parent))) {
            if (nodeType.elem(parent)) {
                result.push(parent);
            }
        }

        return result;
    },

    // Parent ------
    getParent = function(context) {
        var idx    = 0,
            len    = context.length,
            result = [];
        for (; idx < len; idx++) {
            var parent = getNodeParent(context[idx]);
            if (parent) { result.push(parent); }
        }
        return result;
    },

    // Safely get parent node
    getNodeParent = function(node) {
        return node && node.parentNode;
    },

    _prevNextCrawl = function(method) {
        return function(node) {
            var current = node;
            while ((current = current[method]) && !nodeType.elem(current)) {}
            return current;
        };
    },
    getPrev = _prevNextCrawl('previousSibling'),
    getNext = _prevNextCrawl('nextSibling'),

    _prevNextCrawlAll = function(method) {
        return function(node) {
            var result  = [],
                current = node;
            while ((current = current[method])) {
                if (nodeType.elem(current)) {
                    result.push(current);
                }
            }
            return result;
        };
    },
    getPrevAll = _prevNextCrawlAll('previousSibling'),
    getNextAll = _prevNextCrawlAll('nextSibling'),

    getPositional = function(getter, d, selector) {
        var result = [],
            idx,
            len = d.length,
            sibling;

        for (idx = 0; idx < len; idx++) {
            sibling = getter(d[idx]);
            if (sibling && (!selector || Fizzle.is(selector).match(sibling))) {
                result.push(sibling);
            }
        }

        return result;
    },

    getPositionalAll = function(getter, d, selector) {
        var result = [],
            idx,
            len = d.length,
            siblings,
            filter;

        if (selector) {
            filter = function(sibling) { return Fizzle.is(selector).match(sibling); };
        }

        for (idx = 0; idx < len; idx++) {
            siblings = getter(d[idx]);
            if (selector) {
                siblings = _.filter(siblings, filter);
            }
            result.push.apply(result, siblings);
        }

        return result;
    },

    getPositionalUntil = function(getter, d, selector) {
        var result = [],
            idx,
            len = d.length,
            siblings,
            iterator;

        if (selector) {
            var is = Fizzle.is(selector);
            iterator = function(sibling) {
                var isMatch = is.match(sibling);
                if (isMatch) {
                    result.push(sibling);
                }
                return isMatch;
            };
        }

        for (idx = 0; idx < len; idx++) {
            siblings = getter(d[idx]);

            if (selector) {
                _.each(siblings, iterator);
            } else {
                result.push.apply(result, siblings);
            }
        }

        return result;
    },

    uniqueSort = function(elems, reverse) {
        var result = unique(elems);
        order.sort(result);
        if (reverse) {
            result.reverse();
        }
        return D(result);
    },

    filterAndSort = function(elems, selector, reverse) {
        return uniqueSort(selectorFilter(elems, selector), reverse);
    };

exports.fn = {
    contents: function() {
        return D(
            _.flatten(
                _.pluck(this, 'childNodes')
            )
        );
    },

    index: function(selector) {
        if (!this.length) {
            return -1;
        }

        if (isString(selector)) {
            var first = this[0];
            return D(selector).indexOf(first);
        }

        if (isElement(selector) || isWindow(selector) || isDocument(selector)) {
            return this.indexOf(selector);
        }

        if (isD(selector)) {
            return this.indexOf(selector[0]);
        }

        // fallback
        var first  = this[0],
            parent = first.parentNode;

        if (!parent) {
            return -1;
        }

        // isAttached check to pass test "Node without parent returns -1"
        // nodeType check to pass "If D#index called on element whose parent is fragment, it still should work correctly"
        var attached         = isAttached(first),
            isParentFragment = nodeType.doc_frag(parent);

        if (!attached && !isParentFragment) {
            return -1;
        }

        var childElems = parent.children || _.filter(parent.childNodes, nodeType.elem);

        return [].indexOf.call(childElems, first);
    },

    closest: function(selector, context) {
        return uniqueSort(getClosest(this, selector, context));
    },

    parent: function(selector) {
        return filterAndSort(getParent(this), selector);
    },

    parents: function(selector) {
        return filterAndSort(getParents(this), selector, true);
    },

    parentsUntil: function(stopSelector) {
        return uniqueSort(getParentsUntil(this, stopSelector), true);
    },

    siblings: function(selector) {
        return filterAndSort(getSiblings(this), selector);
    },

    children: function(selector) {
        return filterAndSort(getChildren(this), selector);
    },

    prev: function(selector) {
        return uniqueSort(getPositional(getPrev, this, selector));
    },

    next: function(selector) {
        return uniqueSort(getPositional(getNext, this, selector));
    },

    prevAll: function(selector) {
        return uniqueSort(getPositionalAll(getPrevAll, this, selector), true);
    },

    nextAll: function(selector) {
        return uniqueSort(getPositionalAll(getNextAll, this, selector));
    },

    prevUntil: function(selector) {
        return uniqueSort(getPositionalUntil(getPrevAll, this, selector), true);
    },

    nextUntil: function(selector) {
        return uniqueSort(getPositionalUntil(getNextAll, this, selector));
    }
};
<commit_msg>build crawl functions<commit_after>var _                 = require('_'),
    D                 = require('D'),
    nodeType          = require('nodeType'),
    isString          = require('is/string'),
    isAttached        = require('is/attached'),
    isElement         = require('is/element'),
    isWindow          = require('is/window'),
    isDocument        = require('is/document'),
    isD               = require('is/D'),
    order             = require('order'),
    unique            = require('./array/unique'),
    selectorFilter    = require('./selectors/filter'),
    Fizzle            = require('Fizzle');

var getSiblings = function(context) {
        var idx    = 0,
            len    = context.length,
            result = [];
        for (; idx < len; idx++) {
            var sibs = _getNodeSiblings(context[idx]);
            if (sibs.length) { result.push(sibs); }
        }
        return _.flatten(result);
    },

    _getNodeSiblings = function(node) {
        var parent = node.parentNode;

        if (!parent) {
            return [];
        }

        var sibs = _.toArray(parent.children),
            idx  = sibs.length;

        while (idx--) {
            // Exclude the node itself from the list of its parent's children
            if (sibs[idx] === node) {
                sibs.splice(idx, 1);
            }
        }

        return sibs;
    },

    // Children ------
    getChildren = (arr) => _.flatten(_.map(arr, _children)),
    _children = function(elem) {
        var kids = elem.children,
            idx  = 0, len  = kids.length,
            arr  = new Array(len);
        for (; idx < len; idx++) {
            arr[idx] = kids[idx];
        }
        return arr;
    },

    // Parents ------
    getClosest = function(elems, selector, context) {
        var idx = 0,
            len = elems.length,
            parents,
            closest,
            result = [];
        for (; idx < len; idx++) {
            parents = _crawlUpNode(elems[idx], context);
            parents.unshift(elems[idx]);
            closest = selectorFilter(parents, selector);
            if (closest.length) {
                result.push(closest[0]);
            }
        }
        return _.flatten(result);
    },

    getParents = function(context) {
        var idx = 0,
            len = context.length,
            parents,
            result = [];
        for (; idx < len; idx++) {
            parents = _crawlUpNode(context[idx]);
            result.push(parents);
        }
        return _.flatten(result);
    },

    getParentsUntil = function(d, stopSelector) {
        var idx = 0,
            len = d.length,
            parents,
            result = [];
        for (; idx < len; idx++) {
            parents = _crawlUpNode(d[idx], null, stopSelector);
            result.push(parents);
        }
        return _.flatten(result);
    },

    // Safely get parent node
    _getNodeParent = (node) => node && node.parentNode,

    _crawlUpNode = function(node, context, stopSelector) {
        var result = [],
            parent = node;

        while ((parent   = _getNodeParent(parent))   &&
               !nodeType.doc(parent)                 &&
               (!context      || parent !== context) &&
               (!stopSelector || !Fizzle.is(stopSelector).match(parent))) {
            if (nodeType.elem(parent)) {
                result.push(parent);
            }
        }

        return result;
    },

    // Parent ------
    getParent = function(context) {
        var idx    = 0,
            len    = context.length,
            result = [];
        for (; idx < len; idx++) {
            var parent = _getNodeParent(context[idx]);
            if (parent) { result.push(parent); }
        }
        return result;
    },

    _prevNextCrawl = function(method) {
        return function(node) {
            var current = node;
            while ((current = current[method]) && !nodeType.elem(current)) {}
            return current;
        };
    },
    getPrev = _prevNextCrawl('previousSibling'),
    getNext = _prevNextCrawl('nextSibling'),

    _prevNextCrawlAll = function(method) {
        return function(node) {
            var result  = [],
                current = node;
            while ((current = current[method])) {
                if (nodeType.elem(current)) {
                    result.push(current);
                }
            }
            return result;
        };
    },
    getPrevAll = _prevNextCrawlAll('previousSibling'),
    getNextAll = _prevNextCrawlAll('nextSibling'),

    getPositional = function(getter, d, selector) {
        var result = [],
            idx,
            len = d.length,
            sibling;

        for (idx = 0; idx < len; idx++) {
            sibling = getter(d[idx]);
            if (sibling && (!selector || Fizzle.is(selector).match(sibling))) {
                result.push(sibling);
            }
        }

        return result;
    },

    getPositionalAll = function(getter, d, selector) {
        var result = [],
            idx,
            len = d.length,
            siblings,
            filter;

        if (selector) {
            filter = function(sibling) { return Fizzle.is(selector).match(sibling); };
        }

        for (idx = 0; idx < len; idx++) {
            siblings = getter(d[idx]);
            if (selector) {
                siblings = _.filter(siblings, filter);
            }
            result.push.apply(result, siblings);
        }

        return result;
    },

    getPositionalUntil = function(getter, d, selector) {
        var result = [],
            idx,
            len = d.length,
            siblings,
            iterator;

        if (selector) {
            var is = Fizzle.is(selector);
            iterator = function(sibling) {
                var isMatch = is.match(sibling);
                if (isMatch) {
                    result.push(sibling);
                }
                return isMatch;
            };
        }

        for (idx = 0; idx < len; idx++) {
            siblings = getter(d[idx]);

            if (selector) {
                _.each(siblings, iterator);
            } else {
                result.push.apply(result, siblings);
            }
        }

        return result;
    },

    uniqueSort = function(elems, reverse) {
        var result = unique(elems);
        order.sort(result);
        if (reverse) {
            result.reverse();
        }
        return D(result);
    },

    filterAndSort = function(elems, selector, reverse) {
        return uniqueSort(selectorFilter(elems, selector), reverse);
    };

exports.fn = {
    contents: function() {
        return D(
            _.flatten(
                _.pluck(this, 'childNodes')
            )
        );
    },

    index: function(selector) {
        if (!this.length) {
            return -1;
        }

        if (isString(selector)) {
            var first = this[0];
            return D(selector).indexOf(first);
        }

        if (isElement(selector) || isWindow(selector) || isDocument(selector)) {
            return this.indexOf(selector);
        }

        if (isD(selector)) {
            return this.indexOf(selector[0]);
        }

        // fallback
        var first  = this[0],
            parent = first.parentNode;

        if (!parent) {
            return -1;
        }

        // isAttached check to pass test "Node without parent returns -1"
        // nodeType check to pass "If D#index called on element whose parent is fragment, it still should work correctly"
        var attached         = isAttached(first),
            isParentFragment = nodeType.doc_frag(parent);

        if (!attached && !isParentFragment) {
            return -1;
        }

        var childElems = parent.children || _.filter(parent.childNodes, nodeType.elem);

        return [].indexOf.call(childElems, first);
    },

    closest: function(selector, context) {
        return uniqueSort(getClosest(this, selector, context));
    },

    parent: function(selector) {
        return filterAndSort(getParent(this), selector);
    },

    parents: function(selector) {
        return filterAndSort(getParents(this), selector, true);
    },

    parentsUntil: function(stopSelector) {
        return uniqueSort(getParentsUntil(this, stopSelector), true);
    },

    siblings: function(selector) {
        return filterAndSort(getSiblings(this), selector);
    },

    children: function(selector) {
        return filterAndSort(getChildren(this), selector);
    },

    prev: function(selector) {
        return uniqueSort(getPositional(getPrev, this, selector));
    },

    next: function(selector) {
        return uniqueSort(getPositional(getNext, this, selector));
    },

    prevAll: function(selector) {
        return uniqueSort(getPositionalAll(getPrevAll, this, selector), true);
    },

    nextAll: function(selector) {
        return uniqueSort(getPositionalAll(getNextAll, this, selector));
    },

    prevUntil: function(selector) {
        return uniqueSort(getPositionalUntil(getPrevAll, this, selector), true);
    },

    nextUntil: function(selector) {
        return uniqueSort(getPositionalUntil(getNextAll, this, selector));
    }
};
<|endoftext|>


<commit_before>import Model from 'core/infrastructure/model';
import GridService from 'core/services/grid';
import Pipe from 'core/pipe/pipe';
import {getFactory} from './value';

export default class Grid {
	static model() {
		return new Model();
	}

	static service(model) {
		return new GridService(model, getFactory);
	}

	static get pipe() {
		return Pipe;
	}
}<commit_msg>Add evalasync<commit_after>import Model from 'core/infrastructure/model';
import GridService from 'core/services/grid';
import Pipe from 'core/pipe/pipe';
import Log from 'core/infrastructure/log';
import {getFactory} from './value';

export default class Grid {
	constructor($rootScope) {
		this.$rootScope = $rootScope;
	}

	model() {
		return new Model();
	}

	service(model) {
		const $rootScope = this.$rootScope;
		const apply = () => {
			Log.info('service', '$digest apply');
			$rootScope.$evalAsync(angular.noop);
		};

		return new GridService(model, getFactory, apply);
	}

	get pipe() {
		return Pipe;
	}
}

Grid.$inject = ['$rootScope'];<|endoftext|>


<commit_before>import React, { Component } from 'react';

import { Link, graphql } from 'gatsby';
import dayjs from 'dayjs';
import Tag from '../../components/Tag';
import Header from '../../components/Header';
import ShareBox from '../../components/ShareBox';
import SEO from '../../components/SEO';

import { url } from '../../../data/config';

import wrapLayout from '../../api/layout';

const splitTag = (raw = '') => raw.split(', ');

const parseDate = date => dayjs(date).format('YYYY/MM/DD');
const tagCenter = 'col-12 col-md-10 col-lg-8 m-auto';

const getTag = (item) => {
  const { tags, url, createdDate } = item.node;

  if (tags) {
    const date = parseDate(createdDate);
    const postPath = url === 'about' ? url : `${date}/${url}`;
    return {
      tags: splitTag(item.node.tags),
      title: item.node.title,
      url: postPath,
      createdDate,
    };
  }
  return item;
};

const lenOf = (array = []) => array.length;

const Item = ({ url = '', title = '', createdDate = '' }) => (

    <Link href={url} to={url}>
      {title} ({parseDate(createdDate)})
    </Link>

);

const TagSession = ({
  tag = 'tag',
  articles = [],
  isActive = false,
}) => (

      {tag}:

      {articles.map(article => (
        <Item
          url={article.url}
          title={article.title}
          createdDate={article.createdDate}
          key={article.title}
        />
      ))}

);

const style = {
  display: 'flex',
  alignItems: 'center',
  flexDirection: 'row',
};

class TagPage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tags: {},
      showAllTags: false,
    };
  }

  componentWillMount() {
    const tags = {};
    const { edges } = this.props.data.tags;
    const temp = edges.map(item => getTag(item));

    temp.forEach((x) => {
      const { title, url, createdDate } = x;

      for (let i = 0, n = x.tags.length; i < n; i += 1) {
        const item = { title, url, createdDate };
        if (tags[x.tags[i]]) {
          tags[x.tags[i]].push(item);
        } else {
          tags[x.tags[i]] = [item];
        }
      }
    });
    this.setState({ tags });
  }

  compareTag(a, b) {
    return lenOf(this.state.tags[b]) - lenOf(this.state.tags[a]);
  }

  toggleAllTags() {
    this.setState(state => ({
      showAllTags: !state.showAllTags,
    }));
  }

  render() {
    const rawTags = Object.keys(this.state.tags);
    const tags = rawTags.sort((a, b) => this.compareTag(a, b));

    const hotTags =
      this.state.showAllTags === false ? rawTags.slice(0, 5) : rawTags;

    const { header } = this.props.data;

    const tagUrl = `${url}/tags`;

    return (

            最熱門標籤：
            <button
              type="button"
              className="btn btn-info"
              onClick={() => this.toggleAllTags()}
            >
              展示所有標籤
            </button>

            {hotTags.map(item => (
              <Tag
                name={item}
                count={this.state.tags[item].length}
                key={item}
              />
            ))}

        {tags.map(tag => (
          <TagSession
            tag={tag}
            articles={this.state.tags[tag].filter((v, i, a) => a.indexOf(v) === i,)}
            isActive={decodeURI(this.props.location.hash) === `#${tag}`}
            key={tag}
          />
        ))}

        <ShareBox url={tagUrl} hasCommentBox={false} />

        <SEO
          title={header.title}
          siteTitleAlt={header.title}
          description="所有文章"
          url={tagUrl}
          isPost={false}
          image={header.headerImage}
        />

    );
  }
}

export const pageQuery = graphql`
  query myTags {
    header(purpose: { eq: "Tags" }) {
      headerImage
      title
      titleVisible
      subTitle
      subTitleVisible
    }
    tags: allContentfulMarkdown {
      edges {
        node {
          tags
          title
          url
          createdDate
        }
      }
    }
  }
`;

export default wrapLayout(TagPage);
<commit_msg>🍪 Add propTypes for TagPage<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { Link, graphql } from 'gatsby';
import dayjs from 'dayjs';
import Tag from '../../components/Tag';
import Header from '../../components/Header';
import ShareBox from '../../components/ShareBox';
import SEO from '../../components/SEO';

import { url } from '../../../data/config';

import wrapLayout from '../../api/layout';

const splitTag = (raw = '') => raw.split(', ');

const parseDate = date => dayjs(date).format('YYYY/MM/DD');
const tagCenter = 'col-12 col-md-10 col-lg-8 m-auto';

const getTag = (item) => {
  const { tags, url, createdDate } = item.node;

  if (tags) {
    const date = parseDate(createdDate);
    const postPath = url === 'about' ? url : `${date}/${url}`;
    return {
      tags: splitTag(item.node.tags),
      title: item.node.title,
      url: postPath,
      createdDate,
    };
  }
  return item;
};

const lenOf = (array = []) => array.length;

const Item = ({ url = '', title = '', createdDate = '' }) => (

    <Link href={url} to={url}>
      {title} ({parseDate(createdDate)})
    </Link>

);

const TagSession = ({
  tag = 'tag',
  articles = [],
  isActive = false,
}) => (

      {tag}:

      {articles.map(article => (
        <Item
          url={article.url}
          title={article.title}
          createdDate={article.createdDate}
          key={article.title}
        />
      ))}

);

const style = {
  display: 'flex',
  alignItems: 'center',
  flexDirection: 'row',
};

class TagPage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tags: {},
      showAllTags: false,
    };
  }

  componentWillMount() {
    const tags = {};
    const { edges } = this.props.data.tags;
    const temp = edges.map(item => getTag(item));

    temp.forEach((x) => {
      const { title, url, createdDate } = x;

      for (let i = 0, n = x.tags.length; i < n; i += 1) {
        const item = { title, url, createdDate };
        if (tags[x.tags[i]]) {
          tags[x.tags[i]].push(item);
        } else {
          tags[x.tags[i]] = [item];
        }
      }
    });
    this.setState({ tags });
  }

  compareTag(a, b) {
    return lenOf(this.state.tags[b]) - lenOf(this.state.tags[a]);
  }

  toggleAllTags() {
    this.setState(state => ({
      showAllTags: !state.showAllTags,
    }));
  }

  render() {
    const rawTags = Object.keys(this.state.tags);
    const tags = rawTags.sort((a, b) => this.compareTag(a, b));

    const hotTags =
      this.state.showAllTags === false ? rawTags.slice(0, 5) : rawTags;

    const { header } = this.props.data;

    const tagUrl = `${url}/tags`;

    return (

            最熱門標籤：
            <button
              type="button"
              className="btn btn-info"
              onClick={() => this.toggleAllTags()}
            >
              展示所有標籤
            </button>

            {hotTags.map(item => (
              <Tag
                name={item}
                count={this.state.tags[item].length}
                key={item}
              />
            ))}

        {tags.map(tag => (
          <TagSession
            tag={tag}
            articles={this.state.tags[tag].filter((v, i, a) => a.indexOf(v) === i,)}
            isActive={decodeURI(this.props.location.hash) === `#${tag}`}
            key={tag}
          />
        ))}

        <ShareBox url={tagUrl} hasCommentBox={false} />

        <SEO
          title={header.title}
          siteTitleAlt={header.title}
          description="所有文章"
          url={tagUrl}
          isPost={false}
          image={header.headerImage}
        />

    );
  }
}

TagPage.propTypes = {
  data: PropTypes.shape({
    tags: PropTypes.shape({
      edges: PropTypes.shape({
        node: PropTypes.shape({
          tags: PropTypes.string,
          title: PropTypes.string,
          url: PropTypes.string,
          createdDate: PropTypes.string,
        }),
      }),
    }),
  }),
}

export const pageQuery = graphql`
  query myTags {
    header(purpose: { eq: "Tags" }) {
      headerImage
      title
      titleVisible
      subTitle
      subTitleVisible
    }
    tags: allContentfulMarkdown {
      edges {
        node {
          tags
          title
          url
          createdDate
        }
      }
    }
  }
`;

export default wrapLayout(TagPage);
<|endoftext|>


<commit_before>Object.shallowCopyTo({
	//read
	isEmpty: function()
	{
		return this.length == 0;
	},

	isEqual: function(otherArray)
	{
		if(this.length != otherArray.length) { return false; }

		for (var i = 0; i < this.length; i ++)
		{
			if(this[i] != otherArray[i]) return false;
		}

		return true;
	},

	size: function()
	{
		return this.length;
	},

	at: function(index)
	{
		if(index > -1)
		{
			return this[index];
		}
		else
		{
			return this[this.length + index];
		}
	},

	first: function()
	{
		return this[0];
	},

	rest: function()
	{
		return this.slice(1);
	},

	last: function()
	{
		return this[this.length - 1];
	},

	contains: function(element)
	{
		return this.indexOf(element) > -1;
	},

	hasPrefix: function(otherArray)
	{
		if(this.length < otherArray.length) { return false; }

		for (var i = 0; i < this.length; i ++)
		{
			if(this[i] != otherArray[i]) return false;
		}

		return true;
	},

	itemAfter: function(v)
	{
		var i = this.indexOf(v);
		if(i == -1) return null;
		i = i + 1;
		if(i > this.length - 1) return null;
		if(this[i] != undefined) { return this[i]; }
		return null;
	},

	itemBefore: function(v)
	{
		var i = this.indexOf(v);
		if(i == -1) return null;
		i = i - 1;
		if(i < 0) return null;
		if(this[i]) { return this[i]; }
		return null;
	},

	copy: function()
	{
		return this.slice();
	},

	split: function(subArrayCount)
	{
		var subArrays = [];

		var subArraySize = Math.ceil(this.length / subArrayCount);
		for (var i = 0; i < this.length; i += subArraySize)
		{
			var subArray = this.slice(i, i + subArraySize);
			if(subArray.length < subArraySize)
			{
				var lastSubArray = subArrays.pop();
				if(lastSubArray)
				{
					subArray = lastSubArray.concat(subArray);
				}
			}
			subArrays.push(subArray);
		}

		return subArrays;
	},

	//write

	atInsert: function(i, e)
	{
		this.splice(i, 0, e);
	},

	append: function()
	{
		this.appendItems.call(this, arguments);
		return this;
	},

	appendItems: function(elements)
	{
		this.push.apply(this, elements);
		return this;
	},

	appendItemsIfAbsent: function(elements)
	{
		this.appendIfAbsent.apply(this, elements);
		return this;
	},

	prepend: function(e)
	{
		this.unshift(e);
		return this;
	},

	appendIfAbsent: function()
	{
		var self = this;;
		this.slice.call(arguments).forEach(function(value)
		{
			if(self.indexOf(value) == -1)
			{
				self.push(value);
				return true;
			}
		})

		return false;
	},

	removeAt: function(i)
	{
		this.splice(i, 1);
		return this;
	},

	remove: function(e)
	{
		var i = this.indexOf(e);
		if(i > -1)
		{
			this.removeAt(i);
		}
		return this;
	},

	emptiesRemoved: function() {
	  return this.filter(function(e){
	    return e;
	  });
	},

	removeFirst: function ()
	{
		return this.shift();
	},

	removeLast: function()
	{
		return this.pop();
	},

	removeItems: function(elements)
	{
		elements.forEach(function(e){ this.remove(e) }, this);
		return this;
	},

	empty: function()
	{
		this.splice(0, this.length);
		return this;
	},

	replace: function(obj, withObj)
	{
		var i = this.indexOf(obj);
		if (i > -1)
		{
			this.removeAt(i);
			this.atInsert(i, withObj);
		}
		return this;
	},

	swap: function(e1, e2)
	{
		var i1 = this.indexOf(e1);
		var i2 = this.indexOf(e2);

		this[i1] = e2;
		this[i2] = e1;

		return this;
	},

	shuffle: function()
	{
		var i = this.length;
		if(i == 0) return false;
		while (-- i)
		{
			var j = Math.floor(Math.random() * ( i + 1 ));
			var tempi = this[i];
			var tempj = this[j];
			this[i] = tempj;
			this[j] = tempi;
		}

		return this;
	},

	atRandom: function()
	{
		return this[Math.floor(Math.random() * this.length)];
	},

	//iterate
	forEachCall: function(functionName)
	{
		var args = this.slice.call(arguments).slice(1);
		args.push(0);
		this.forEach(function(e, i)
		{
			args[args.length - 1] = i;
			if (e)
			{
				e[functionName].apply(e, args);
			}
		});
		return this;
	},

	forEachPerform: function()
	{
		return this.forEachCall.apply(this, arguments);
	},

	sortPerform: function(functionName)
	{
		var args = this.slice.call(arguments).slice(1);
		return this.sort(function(x, y)
		{
			var xRes = x[functionName].apply(x, args);
			var yRes = y[functionName].apply(y, args);
			if(xRes < yRes)
			{
				return -1;
			}
			else if(yRes < xRes)
			{
				return 1;
			}
			else
			{
				return 0;
			}
		});
	},

	parallelPerform: function() {
	  var args = this.slice.call(arguments).slice();
	  var fn = args.pop();

	  if (this.length == 0) {
	    fn(null);
	    return;
	  }

	  var remaining = this.length;
	  var err = null;
	  var self = this;
	  args.push(function(error){
	    err = error;
	    remaining --;
	    if (remaining == 0) {
	      fn(err, ops);
	    }
	  });

	  var ops = this.mapPerform.apply(this, args);
	  return this;
	},

	serialPerform: function(functionName) {
	  var args = this.slice.call(arguments).slice(1);
	  var fn = args.pop();

	  if (this.length == 0) {
	    fn(null);
	    return;
	  }

	  var i = 0;
	  var self = this;
	  function next() {
	    if (i < self.length) {
	      var e = self[i];
	      i ++;
	      e[functionName].apply(e, args);
	    } else {
	      fn();
	    }
	  };

	  args.push(function(e){
	    if (e) {
	      fn(e);
	    } else {
	      next();
	    }
	  });

	  next();

	  return this;
	},

	mapPerform: function(functionName)
	{
		var args = this.slice.call(arguments).slice(1);
		args.push(0);
		return this.map(function(e, i)
		{
			args[args.length - 1] = i; //TODO: maybe a bad idea if functions don't expect this
			return e[functionName].apply(e, args);
		});
	},

	mapProperty: function(propertyName)
	{
		return this.map(function(e){
		  return e[propertyName];
		});
	},

	detect: function(callback)
	{
		for (var i = 0; i < this.length; i++)
		{
			if(callback(this[i]))
			{
				return this[i];
			}
		}

		return null;
	},

	detectPerform: function(functionName)
	{
		var args = this.slice.call(arguments).slice(1);
		return this.detect(function(e, i)
		{
			return e[functionName].apply(e, args);
		});
	},

	detectSlot: function(slotName, slotValue)
	{
		for (var i = 0; i < this.length; i++)
		{
			if (this[i].perform(slotName) == slotValue)
			{
				return this[i];
			}
		}

		return null;
	},

	detectProperty: function(slotName, slotValue)
	{
		for (var i = 0; i < this.length; i++)
		{
			if (this[i][slotName] == slotValue)
			{
				return this[i];
			}
		}

		return null;
	},

	detectIndex: function(callback)
	{
		for (var i = 0; i < this.length; i++)
		{
			if(callback(this[i]))
			{
				return i;
			}
		}

		return null;
	},

	everySlot: function(slotName, expectedValue) {
		return this.every(function(obj){
            return obj.perform(slotName) == expectedValue;
        });
	},

	everyProperty: function(slotName, expectedValue) {
		return this.every(function(obj){
            return obj[slotName] == expectedValue;
        });
	},

	everyPerform: function() {
		var args = arguments;
		return this.every(function(obj){
            return obj.perform.apply(obj, args);
        });
	},

	filterPerform: function(messageName)
	{
		var args = this.slice.call(arguments).slice(1);
		args.push(0);
		return this.filter(function(e, i)
		{
			args[args.length - 1] = i;
			return e[messageName].apply(e, args);
		});
	},

    filterSlot: function(slotName, expectedValue) {
        return this.filter(function(obj){
            return obj.perform(slotName) == expectedValue;
        });
    },

	rejectPerform: function(messageName)
	{
		var args = this.slice.call(arguments).slice(1);
		args.push(0);
		return this.filter(function(e, i)
		{
			args[args.length - 1] = i;
			return !e[messageName].apply(e, args);
		});
	},

	rejectSlot: function(slotName, expectedValue) {
        return this.filter(function(obj){
            return obj.perform(slotName) != expectedValue;
        });
    },

	minValue: function(callback, theDefault)
	{
		var obj = this.min(callback);
		if(obj == undefined)
		{
			return theDefault;
		}
		return callback(obj);
	},

	maxValue: function(callback, theDefault)
	{
		var obj = this.max(callback);
		if(obj == undefined)
		{
			return theDefault;
		}
		return callback(obj);
	},

	max: function(callback)
	{
		var m = undefined;
		var mObject = undefined;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			if(m == undefined || v > m)
			{
				m = v;
				mObject = this[i];
			}
		}

		return mObject;
	},

	maxIndex: function(callback)
	{
		var m = undefined;
		var index = 0;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			if(m == undefined || v > m)
			{
				m = v;
				index = i;
			}
		}

		return index;
	},

	min: function(callback)
	{
		var m = undefined;
		var mObject = undefined;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			if(m == undefined || v < m)
			{
				m = v;
				mObject = this[i];
			}
		}

		return mObject;
	},

	minIndex: function(callback)
	{
		var m = undefined;
		var index = 0;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			if(m == undefined || v < m)
			{
				m = v;
				index = i;
			}
		}

		return index;
	},

	sum: function(callback)
	{
		var m = undefined;
		var sum = 0;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			sum = sum + v;
		}

		return sum;
	},

	average: function()
	{
		return this.sum() / this.length;
	},

	atMidpoint: function()
	{
		return this.at(Math.floor((this.length - 1)/2));
	},

	flatten: function()
	{
		var flattened = [];
		this.forEach(function(array){
			flattened.appendItems(array);
		});
		return flattened;
	},

	clone: function()
	{
		return this.copy();
	},

	unique: function()
	{
		var a = [];
		this.forEach(function(e){
			a.appendIfAbsent(e);
		});
		return a;
	},

	reversed: function()
	{
		return this.copy().reverse();
	},

	asPath: function()
	{
		if (this.length == 1 && this.first() == "")
		{
			return "/";
		}
		else
		{
			return this.join("/");
		}
	},

	isAbsolutePath: function()
	{
		return this.first() == "";
	},

	isRelativePath: function()
	{
		return this.first() != "";
	},

	isArray: true
}, Array.prototype);

Array.wrap = function(obj)
{
	if (obj === null || obj === undefined)
	{
		return [];
	}
	else if (obj.isArray)
	{
		return obj;
	}
	else
	{
		return [obj];
	}
}<commit_msg>Array fixes<commit_after>Object.shallowCopyTo({
	//read
	isEmpty: function()
	{
		return this.length == 0;
	},

	isEqual: function(otherArray)
	{
		if(this.length != otherArray.length) { return false; }

		for (var i = 0; i < this.length; i ++)
		{
			if(this[i] != otherArray[i]) return false;
		}

		return true;
	},

	size: function()
	{
		return this.length;
	},

	at: function(index)
	{
		if(index > -1)
		{
			return this[index];
		}
		else
		{
			return this[this.length + index];
		}
	},

	first: function()
	{
		return this[0];
	},

	rest: function()
	{
		return this.slice(1);
	},

	last: function()
	{
		return this[this.length - 1];
	},

	contains: function(element)
	{
		return this.indexOf(element) > -1;
	},

	hasPrefix: function(otherArray)
	{
		if(this.length < otherArray.length) { return false; }

		for (var i = 0; i < this.length; i ++)
		{
			if(this[i] != otherArray[i]) return false;
		}

		return true;
	},

	itemAfter: function(v)
	{
		var i = this.indexOf(v);
		if(i == -1) return null;
		i = i + 1;
		if(i > this.length - 1) return null;
		if(this[i] != undefined) { return this[i]; }
		return null;
	},

	itemBefore: function(v)
	{
		var i = this.indexOf(v);
		if(i == -1) return null;
		i = i - 1;
		if(i < 0) return null;
		if(this[i]) { return this[i]; }
		return null;
	},

	copy: function()
	{
		return this.slice();
	},

	split: function(subArrayCount)
	{
		var subArrays = [];

		var subArraySize = Math.ceil(this.length / subArrayCount);
		for (var i = 0; i < this.length; i += subArraySize)
		{
			var subArray = this.slice(i, i + subArraySize);
			if(subArray.length < subArraySize)
			{
				var lastSubArray = subArrays.pop();
				if(lastSubArray)
				{
					subArray = lastSubArray.concat(subArray);
				}
			}
			subArrays.push(subArray);
		}

		return subArrays;
	},

	//write

	atInsert: function(i, e)
	{
		this.splice(i, 0, e);
	},

	append: function()
	{
		this.appendItems.call(this, arguments);
		return this;
	},

	appendItems: function(elements)
	{
		this.push.apply(this, elements);
		return this;
	},

	appendItemsIfAbsent: function(elements)
	{
		this.appendIfAbsent.apply(this, elements);
		return this;
	},

	prepend: function(e)
	{
		this.unshift(e);
		return this;
	},

	appendIfAbsent: function()
	{
		var self = this;;
		this.slice.call(arguments).forEach(function(value)
		{
			if(self.indexOf(value) == -1)
			{
				self.push(value);
				return true;
			}
		})

		return false;
	},

	removeAt: function(i)
	{
		this.splice(i, 1);
		return this;
	},

	remove: function(e)
	{
		var i = this.indexOf(e);
		if(i > -1)
		{
			this.removeAt(i);
		}
		return this;
	},

	emptiesRemoved: function() {
	  return this.filter(function(e){
	    return e;
	  });
	},

	removeFirst: function ()
	{
		return this.shift();
	},

	removeLast: function()
	{
		return this.pop();
	},

	removeItems: function(elements)
	{
		elements.forEach(function(e){ this.remove(e) }, this);
		return this;
	},

	empty: function()
	{
		this.splice(0, this.length);
		return this;
	},

	replace: function(obj, withObj)
	{
		var i = this.indexOf(obj);
		if (i > -1)
		{
			this.removeAt(i);
			this.atInsert(i, withObj);
		}
		return this;
	},

	swap: function(e1, e2)
	{
		var i1 = this.indexOf(e1);
		var i2 = this.indexOf(e2);

		this[i1] = e2;
		this[i2] = e1;

		return this;
	},

	shuffle: function()
	{
		var i = this.length;
		if(i == 0) return false;
		while (-- i)
		{
			var j = Math.floor(Math.random() * ( i + 1 ));
			var tempi = this[i];
			var tempj = this[j];
			this[i] = tempj;
			this[j] = tempi;
		}

		return this;
	},

	atRandom: function()
	{
		return this[Math.floor(Math.random() * this.length)];
	},

	//iterate
	forEachCall: function(functionName)
	{
		var args = this.slice.call(arguments).slice(1);
		args.push(0);
		this.forEach(function(e, i)
		{
			args[args.length - 1] = i;
			if (e)
			{
				e[functionName].apply(e, args);
			}
		});
		return this;
	},

	forEachPerform: function()
	{
		return this.forEachCall.apply(this, arguments);
	},

	sortPerform: function(functionName)
	{
		var args = this.slice.call(arguments).slice(1);
		return this.sort(function(x, y)
		{
			var xRes = x[functionName].apply(x, args);
			var yRes = y[functionName].apply(y, args);
			if(xRes < yRes)
			{
				return -1;
			}
			else if(yRes < xRes)
			{
				return 1;
			}
			else
			{
				return 0;
			}
		});
	},

	parallelPerform: function() {
	  var args = this.slice.call(arguments).slice();
	  var fn = args.pop();

	  if (this.length == 0) {
	    fn(null);
	    return;
	  }

	  var remaining = this.length;
	  var err = null;
	  var self = this;
	  args.push(function(error){
	    err = error;
	    remaining --;
	    if (remaining == 0) {
	      fn(err, ops);
	    }
	  });

	  var ops = this.mapPerform.apply(this, args);
	  return this;
	},

	serialPerform: function(functionName) {
	  var args = this.slice.call(arguments).slice(1);
	  var fn = args.pop();

	  if (this.length == 0) {
	    fn(null);
	    return;
	  }

	  var i = 0;
	  var self = this;
	  function next() {
	    if (i < self.length) {
	      var e = self[i];
	      i ++;
	      e[functionName].apply(e, args);
	    } else {
	      fn();
	    }
	  };

	  args.push(function(e){
	    if (e) {
	      fn(e);
	    } else {
	      next();
	    }
	  });

	  next();

	  return this;
	},

	mapPerform: function()
	{
		var args = Array.prototype.slice.call(arguments);
		args.unshift(null);
		args.push(-1);

		return this.map(function(obj, i) {
			args[0] = obj;
			args[args.length - 1] = i; //TODO: should we append i?  Methods might not expect this ...
			return Object.perform.apply(Object, args);
		});
	},

	mapProperty: function(propertyName)
	{
		return this.map(function(e){
		  return e[propertyName];
		});
	},

	detect: function(callback)
	{
		for (var i = 0; i < this.length; i++)
		{
			if(callback(this[i]))
			{
				return this[i];
			}
		}

		return null;
	},

	detectPerform: function(functionName)
	{
		var args = this.slice.call(arguments).slice(1);
		return this.detect(function(e, i)
		{
			return e[functionName].apply(e, args);
		});
	},

	detectSlot: function(slotName, slotValue)
	{
		for (var i = 0; i < this.length; i++)
		{
			if (this[i].perform(slotName) == slotValue)
			{
				return this[i];
			}
		}

		return null;
	},

	detectProperty: function(slotName, slotValue)
	{
		for (var i = 0; i < this.length; i++)
		{
			if (this[i][slotName] == slotValue)
			{
				return this[i];
			}
		}

		return null;
	},

	detectIndex: function(callback)
	{
		for (var i = 0; i < this.length; i++)
		{
			if(callback(this[i]))
			{
				return i;
			}
		}

		return null;
	},

	everySlot: function(slotName, expectedValue) {
		return this.every(function(obj){
            return obj.perform(slotName) == expectedValue;
        });
	},

	everyProperty: function(slotName, expectedValue) {
		var args = arguments;
		return this.every(function(obj){
			if (args.length == 2) {
				return obj[slotName] == expectedValue;
			} else {
				return obj[slotName];
			}
        });
	},

	everyPerform: function() {
		var args = arguments;
		return this.every(function(obj){
            return obj.perform.apply(obj, args);
        });
	},

	filterPerform: function(messageName)
	{
		var args = this.slice.call(arguments).slice(1);
		args.push(0);
		return this.filter(function(e, i)
		{
			args[args.length - 1] = i;
			return e[messageName].apply(e, args);
		});
	},

    filterSlot: function(slotName, expectedValue) {
        return this.filter(function(obj){
            return obj.perform(slotName) == expectedValue;
        });
    },

    filterProperty: function(slotName, expectedValue) {
    	var args = arguments;
        return this.filter(function(obj){
            if (args.length == 2) {
				return obj[slotName] == expectedValue;
			} else {
				return obj[slotName];
			}
        });
    },

	rejectPerform: function(messageName)
	{
		var args = this.slice.call(arguments).slice(1);
		args.push(0);
		return this.filter(function(e, i)
		{
			args[args.length - 1] = i;
			return !e[messageName].apply(e, args);
		});
	},

	rejectSlot: function(slotName, expectedValue) {
        return this.filter(function(obj){
            return obj.perform(slotName) != expectedValue;
        });
    },

	minValue: function(callback, theDefault)
	{
		var obj = this.min(callback);
		if(obj == undefined)
		{
			return theDefault;
		}
		return callback(obj);
	},

	maxValue: function(callback, theDefault)
	{
		var obj = this.max(callback);
		if(obj == undefined)
		{
			return theDefault;
		}
		return callback(obj);
	},

	max: function(callback)
	{
		var m = undefined;
		var mObject = undefined;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			if(m == undefined || v > m)
			{
				m = v;
				mObject = this[i];
			}
		}

		return mObject;
	},

	maxIndex: function(callback)
	{
		var m = undefined;
		var index = 0;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			if(m == undefined || v > m)
			{
				m = v;
				index = i;
			}
		}

		return index;
	},

	min: function(callback)
	{
		var m = undefined;
		var mObject = undefined;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			if(m == undefined || v < m)
			{
				m = v;
				mObject = this[i];
			}
		}

		return mObject;
	},

	minIndex: function(callback)
	{
		var m = undefined;
		var index = 0;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			if(m == undefined || v < m)
			{
				m = v;
				index = i;
			}
		}

		return index;
	},

	sum: function(callback)
	{
		var m = undefined;
		var sum = 0;
		var length = this.length;

		for (var i = 0; i < length; i++)
		{
			var v = this[i];
			if(callback) v = callback(v);

			sum = sum + v;
		}

		return sum;
	},

	average: function()
	{
		return this.sum() / this.length;
	},

	atMidpoint: function()
	{
		return this.at(Math.floor((this.length - 1)/2));
	},

	flatten: function()
	{
		var flattened = [];
		this.forEach(function(array){
			flattened.appendItems(array);
		});
		return flattened;
	},

	clone: function()
	{
		return this.copy();
	},

	unique: function()
	{
		var a = [];
		this.forEach(function(e){
			a.appendIfAbsent(e);
		});
		return a;
	},

	reversed: function()
	{
		return this.copy().reverse();
	},

	asPath: function()
	{
		if (this.length == 1 && this.first() == "")
		{
			return "/";
		}
		else
		{
			return this.join("/");
		}
	},

	isAbsolutePath: function()
	{
		return this.first() == "";
	},

	isRelativePath: function()
	{
		return this.first() != "";
	},

	isArray: true
}, Array.prototype);

Array.wrap = function(obj)
{
	if (obj === null || obj === undefined)
	{
		return [];
	}
	else if (obj.isArray)
	{
		return obj;
	}
	else
	{
		return [obj];
	}
}<|endoftext|>


<commit_before>import assert from 'assert';
import mongoose from 'mongoose';
import fp from 'mostly-func';

const defaultOptions = {
  classify: null,            // classify position by a specified field
  unshift: false             // insert at first
};

export default function (schema, options) {
  options = fp.assignAll(defaultOptions, options);

  if (!schema.get('position')) {
    schema.add({ position: { type: Number } });
  }

  schema.index({ position: 1 });

  const preUpdate = function (item, Model, next) {
    if (fp.isValid(item.position)) {
      return next();
    }

    const addLast = function (done) {
      let query = Model.findOne();
      if (options.classify) {
        assert(item[options.classify], 'classify field is not provided with item');
        query.where(options.classify).eq(item[options.classify]);
      }
      query.sort('-position').then(max => {
        item.position = (max && max.position) ? max.position + 1 : 0;
        done();
      });
    };

    if (options.unshift === true) {
      let query = Model.where('position').exists();
      if (options.classify) {
        assert(item[options.classify], 'classify field is not provided with item');
        query.where(options.classify).eq(item[options.classify]);
      }
      query.setOptions({ multi: true })
        .update({
          $inc: { position: 1 }
        }, err => {
          if (err) {
            console.error('sortable $inc error:', err);
            return addLast(next);
          } else {
            item.position = 0;
            return next();
          }
        });
    } else {
      return addLast(next);
    }
  };

  // TODO function model like pre('update')
  schema.pre('save', function (next) {
    let item = this;
    const Model = mongoose.model(item.constructor.modelName);
    preUpdate(item, Model, next);
  });

  schema.pre('update', function (next) {
    const update = this.getUpdate();
    if (this.model && update && update.$set) {
      if (update.$inc && !fp.isNil(update.$inc.position)) {
        return next();
      }
      preUpdate(update.$set, this.model, next);
    } else {
      return next();
    }
  });

  // TODO function model like pre('update')
  schema.pre('findOneAndUpdate', function (next) {
    let item = this.getUpdate();
    const Model = mongoose.model(this.model.modelName);
    preUpdate(item, Model, next);
  });

}<commit_msg>Fix sortable position with soft delete<commit_after>import assert from 'assert';
import mongoose from 'mongoose';
import fp from 'mostly-func';

const defaultOptions = {
  delete: null,        // shift poisiton with soft delete field
  classify: null,      // classify position by a specified field
  unshift: false       // insert at first
};

export default function (schema, options) {
  options = fp.assignAll(defaultOptions, options);

  if (!schema.get('position')) {
    schema.add({ position: { type: Number } });
  }

  schema.index({ position: 1 });

  const preUpdate = function (item, Model, next) {

    const classifyQuery = function (query) {
      if (options.classify) {
        assert(item[options.classify], 'classify field is not provided with item');
        query.where(options.classify).eq(item[options.classify]);
      }
      return query;
    };

    const addLast = function (done) {
      let query = Model.findOne();
      if (options.delete) {
        query.where(options.delete, null);
      }
      classifyQuery(query).sort('-position').then(max => {
        item.position = (max && max.position) ? max.position + 1 : 0;
        done();
      });
    };

    const addFirst = function (done) {
      let query = Model.where('position').exists();
      classifyQuery(query).setOptions({ multi: true })
        .update({
          $inc: { position: 1 }
        }, err => {
          if (err) {
            console.error('sortable $inc error:', err);
            addLast(done);
          } else {
            item.position = 0;
            done();
          }
        });
    };

    // if item has the delete field
    if (options.delete && item[options.delete] !== undefined) {
      if (item[options.delete]) {
        return next();
      } else {
        if (options.unshift === true) {
          return addFirst(next);
        } else {
          return addLast(next);
        }
      }
    }

    if (fp.isValid(item.position)) {
      return next();
    } else {
      if (options.unshift === true) {
        return addFirst(next);
      } else {
        return addLast(next);
      }
    }
  };

  // TODO function model like pre('update')
  schema.pre('save', function (next) {
    let item = this;
    const Model = mongoose.model(item.constructor.modelName);
    preUpdate(item, Model, next);
  });

  schema.pre('update', function (next) {
    const update = this.getUpdate();
    if (this.model && update && update.$set) {
      if (update.$inc && !fp.isNil(update.$inc.position)) {
        return next();
      }
      preUpdate(update.$set, this.model, next);
    } else {
      return next();
    }
  });

  // TODO function model like pre('update')
  schema.pre('findOneAndUpdate', function (next) {
    let item = this.getUpdate();
    const Model = mongoose.model(this.model.modelName);
    preUpdate(item, Model, next);
  });

}<|endoftext|>


<commit_before>/**
 * @file Atom Proxy
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import { Vector3 } from "../../lib/three.es6.js";

import {
    ProteinType, RnaType, DnaType, WaterType, IonType, SaccharideType,
    CgProteinBackboneType, CgRnaBackboneType, CgDnaBackboneType
} from "../structure/structure-constants.js";

/**
 * Atom proxy
 * @class
 * @param {Structure} structure - the structure
 * @param {Integer} index - the index
 */
function AtomProxy( structure, index ){

    this.structure = structure;
    this.chainStore = structure.chainStore;
    this.residueStore = structure.residueStore;
    this.atomStore = structure.atomStore;
    this.residueMap = structure.residueMap;
    this.atomMap = structure.atomMap;
    this.bondHash = structure.bondHash;
    this.index = index;

}

AtomProxy.prototype = {

    constructor: AtomProxy,
    type: "AtomProxy",

    structure: undefined,
    chainStore: undefined,
    residueStore: undefined,
    atomStore: undefined,
    index: undefined,

    get modelIndex () {
        return this.chainStore.modelIndex[ this.chainIndex ];
    },
    get chainIndex () {
        return this.residueStore.chainIndex[ this.residueIndex ];
    },
    get residue () {
        console.warn( "residue - might be expensive" );
        return this.structure.getResidueProxy( this.residueIndex, false );
    },

    get residueIndex () {
        return this.atomStore.residueIndex[ this.index ];
    },
    set residueIndex ( value ) {
        this.atomStore.residueIndex[ this.index ] = value;
    },

    //

    get sstruc () {
        return this.residueStore.getSstruc( this.residueIndex );
    },
    get inscode () {
        return this.residueStore.getInscode( this.residueIndex );
    },
    get resno () {
        return this.residueStore.resno[ this.residueIndex ];
    },
    get chainname () {
        return this.chainStore.getChainname( this.chainIndex );
    },

    //

    get residueType () {
        return this.residueMap.get( this.residueStore.residueTypeId[ this.residueIndex ] );
    },
    get atomType () {
        return  this.atomMap.get( this.atomStore.atomTypeId[ this.index ] );
    },
    get residueAtomOffset () {
        return this.residueStore.atomOffset[ this.residueIndex ];
    },

    //

    get resname () {
        return this.residueType.resname;
    },
    get hetero () {
        return this.residueType.hetero;
    },

    //

    get atomname () {
        return this.atomType.atomname;
    },
    get element () {
        return this.atomType.element;
    },
    get vdw () {
        return this.atomType.vdw;
    },
    get covalent () {
        return this.atomType.covalent;
    },

    //

    get x () {
        return this.atomStore.x[ this.index ];
    },
    set x ( value ) {
        this.atomStore.x[ this.index ] = value;
    },

    get y () {
        return this.atomStore.y[ this.index ];
    },
    set y ( value ) {
        this.atomStore.y[ this.index ] = value;
    },

    get z () {
        return this.atomStore.z[ this.index ];
    },
    set z ( value ) {
        this.atomStore.z[ this.index ] = value;
    },

    get serial () {
        return this.atomStore.serial[ this.index ];
    },
    set serial ( value ) {
        this.atomStore.serial[ this.index ] = value;
    },

    get bfactor () {
        return this.atomStore.bfactor[ this.index ];
    },
    set bfactor ( value ) {
        this.atomStore.bfactor[ this.index ] = value;
    },

    get occupancy () {
        return this.atomStore.occupancy[ this.index ];
    },
    set occupancy ( value ) {
        this.atomStore.occupancy[ this.index ] = value;
    },

    get altloc () {
        return this.atomStore.getAltloc( this.index );
    },
    set altloc ( value ) {
        this.atomStore.setAltloc( this.index, value );
    },

    //

    eachBond: function( callback ){

        var bp = this.structure._bp;
        var idx = this.index;
        var bondHash = this.bondHash;
        var indexArray = bondHash.indexArray;
        var n = bondHash.countArray[ idx ];
        var offset = bondHash.offsetArray[ idx ];

        for( var i = 0; i < n; ++i ){
            bp.index = indexArray[ offset + i ];
            callback( bp );
        }

    },

    eachBondedAtom: function( callback ){

        var ap = this.structure._ap;
        var idx = this.index;

        this.eachBond( function( bp ){
            if( idx !== bp.atomIndex1 ){
                ap.index = bp.atomIndex1;
            }else{
                ap.index = bp.atomIndex2;
            }
            callback( ap );
        } );

    },

    //

    isBackbone: function(){
        var backboneIndexList = this.residueType.backboneIndexList;
        // console.log(backboneIndexList)
        if( backboneIndexList.length > 0 ){
            var atomOffset = this.residueStore.atomOffset[ this.residueIndex ];
            return backboneIndexList.indexOf( this.index - atomOffset ) !== -1;
        }else{
            return false;
        }
    },

    isPolymer: function(){
        var moleculeType = this.residueType.moleculeType;
        return (
            moleculeType === ProteinType ||
            moleculeType === RnaType ||
            moleculeType === DnaType
        );
    },

    isSidechain: function(){
        return this.isPolymer() && !this.isBackbone();
    },

    isCg: function(){
        var backboneType = this.residueType.backboneType;
        return (
            backboneType === CgProteinBackboneType ||
            backboneType === CgRnaBackboneType ||
            backboneType === CgDnaBackboneType
        );
    },

    isHetero: function(){
        return this.residueType.hetero === 1;
    },

    isProtein: function(){
        return this.residueType.moleculeType === ProteinType;
    },

    isNucleic: function(){
        var moleculeType = this.residueType.moleculeType;
        return (
            moleculeType === RnaType ||
            moleculeType === DnaType
        );
    },

    isRna: function(){
        return this.residueType.moleculeType === RnaType;
    },

    isDna: function(){
        return this.residueType.moleculeType === DnaType;
    },

    isWater: function(){
        return this.residueType.moleculeType === WaterType;
    },

    isIon: function(){
        return this.residueType.moleculeType === IonType;
    },

    isSaccharide: function(){
        return this.residueType.moleculeType === SaccharideType;
    },

    isRing: function(){
        var ringFlags = this.residueType.getRings().flags;
        return ringFlags[ this.index - this.residueAtomOffset ] === 1;
    },

    distanceTo: function( atom ){
        var taa = this.atomStore;
        var aaa = atom.atomStore;
        var ti = this.index;
        var ai = atom.index;
        var x = taa.x[ ti ] - aaa.x[ ai ];
        var y = taa.y[ ti ] - aaa.y[ ai ];
        var z = taa.z[ ti ] - aaa.z[ ai ];
        var distSquared = x * x + y * y + z * z;
        return Math.sqrt( distSquared );
    },

    connectedTo: function( atom ){

        var taa = this.atomStore;
        var aaa = atom.atomStore;
        var ti = this.index;
        var ai = atom.index;

        if( taa.altloc && aaa.altloc ){
            var ta = taa.altloc[ ti ];  // use Uint8 value to compare
            var aa = aaa.altloc[ ai ];  // no need to convert to char
            // 0 is the Null character, 32 is the space character
            if( !( ta === 0 || aa === 0 || ta === 32 || aa === 32 || ( ta === aa ) ) ) return false;
        }

        var x = taa.x[ ti ] - aaa.x[ ai ];
        var y = taa.y[ ti ] - aaa.y[ ai ];
        var z = taa.z[ ti ] - aaa.z[ ai ];

        var distSquared = x * x + y * y + z * z;

        // if( this.residue.isCg() ) console.log( this.qualifiedName(), Math.sqrt( distSquared ), distSquared )
        if( distSquared < 64.0 && this.isCg() ) return true;

        if( isNaN( distSquared ) ) return false;

        var d = this.covalent + atom.covalent;
        var d1 = d + 0.3;
        var d2 = d - 0.5;

        return distSquared < ( d1 * d1 ) && distSquared > ( d2 * d2 );

    },

    positionFromArray: function( array, offset ){

        if( offset === undefined ) offset = 0;

        this.x = array[ offset + 0 ];
        this.y = array[ offset + 1 ];
        this.z = array[ offset + 2 ];

        return this;

    },

    positionToArray: function( array, offset ){

        if( array === undefined ) array = [];
        if( offset === undefined ) offset = 0;

        var index = this.index;
        var atomStore = this.atomStore;

        array[ offset + 0 ] = atomStore.x[ index ];
        array[ offset + 1 ] = atomStore.y[ index ];
        array[ offset + 2 ] = atomStore.z[ index ];

        return array;

    },

    positionToVector3: function( v ){

        if( v === undefined ) v = new Vector3();

        v.x = this.x;
        v.y = this.y;
        v.z = this.z;

        return v;

    },

    positionFromVector3: function( v ){

        this.x = v.x;
        this.y = v.y;
        this.z = v.z;

        return this;

    },

    /**
     * Get intra group/residue bonds
     * @param  {Boolean} firstOnly - immediately return the first connected atomIndex
     * @return {Integer[]|Integer|undefined} connected atomIndices
     */
    getResidueBonds: function( firstOnly ){

        var residueAtomOffset = this.residueAtomOffset;
        var relativeIndex = this.index - this.residueAtomOffset;
        var bonds = this.residueType.getBonds();
        var atomIndices1 = bonds.atomIndices1;
        var atomIndices2 = bonds.atomIndices2;
        var idx1, idx2, connectedAtomIndex, connectedAtomIndices;

        if( !firstOnly ) connectedAtomIndices = [];

        idx1 = atomIndices1.indexOf( relativeIndex );
        while( idx1 !== -1 ){
            connectedAtomIndex = atomIndices2[ idx1 ] + residueAtomOffset;
            if( firstOnly ) return connectedAtomIndex;
            connectedAtomIndices.push( connectedAtomIndex );
            idx1 = atomIndices1.indexOf( relativeIndex, idx1 + 1 );
        }

        idx2 = atomIndices2.indexOf( relativeIndex );
        while( idx2 !== -1 ){
            connectedAtomIndex = atomIndices1[ idx2 ] + residueAtomOffset;
            if( firstOnly ) return connectedAtomIndex;
            connectedAtomIndices.push( connectedAtomIndex );
            idx2 = atomIndices2.indexOf( relativeIndex, idx2 + 1 );
        }

        return connectedAtomIndices;

    },

    //

    qualifiedName: function( noResname ){
        var name = "";
        if( this.resname && !noResname ) name += "[" + this.resname + "]";
        if( this.resno !== undefined ) name += this.resno;
        if( this.inscode ) name += "^" + this.inscode;
        if( this.chainname ) name += ":" + this.chainname;
        if( this.atomname ) name += "." + this.atomname;
        if( this.altloc ) name += "%" + this.altloc;
        name += "/" + this.modelIndex;
        return name;
    },

    clone: function(){

        return new this.constructor( this.structure, this.index );

    },

    toObject: function(){

        return {
            index: this.index,
            residueIndex: this.residueIndex,

            atomno: this.atomno,
            resname: this.resname,
            x: this.x,
            y: this.y,
            z: this.z,
            element: this.element,
            chainname: this.chainname,
            resno: this.resno,
            serial: this.serial,
            vdw: this.vdw,
            covalent: this.covalent,
            hetero: this.hetero,
            bfactor: this.bfactor,
            altloc: this.altloc,
            atomname: this.atomname,
            modelindex: this.modelindex
        };

    }

};

export default AtomProxy;
<commit_msg>don't show modelIndex in qualified name when only one model is available<commit_after>/**
 * @file Atom Proxy
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import { Vector3 } from "../../lib/three.es6.js";

import {
    ProteinType, RnaType, DnaType, WaterType, IonType, SaccharideType,
    CgProteinBackboneType, CgRnaBackboneType, CgDnaBackboneType
} from "../structure/structure-constants.js";

/**
 * Atom proxy
 * @class
 * @param {Structure} structure - the structure
 * @param {Integer} index - the index
 */
function AtomProxy( structure, index ){

    this.structure = structure;
    this.chainStore = structure.chainStore;
    this.residueStore = structure.residueStore;
    this.atomStore = structure.atomStore;
    this.residueMap = structure.residueMap;
    this.atomMap = structure.atomMap;
    this.bondHash = structure.bondHash;
    this.index = index;

}

AtomProxy.prototype = {

    constructor: AtomProxy,
    type: "AtomProxy",

    structure: undefined,
    chainStore: undefined,
    residueStore: undefined,
    atomStore: undefined,
    index: undefined,

    get modelIndex () {
        return this.chainStore.modelIndex[ this.chainIndex ];
    },
    get chainIndex () {
        return this.residueStore.chainIndex[ this.residueIndex ];
    },
    get residue () {
        console.warn( "residue - might be expensive" );
        return this.structure.getResidueProxy( this.residueIndex, false );
    },

    get residueIndex () {
        return this.atomStore.residueIndex[ this.index ];
    },
    set residueIndex ( value ) {
        this.atomStore.residueIndex[ this.index ] = value;
    },

    //

    get sstruc () {
        return this.residueStore.getSstruc( this.residueIndex );
    },
    get inscode () {
        return this.residueStore.getInscode( this.residueIndex );
    },
    get resno () {
        return this.residueStore.resno[ this.residueIndex ];
    },
    get chainname () {
        return this.chainStore.getChainname( this.chainIndex );
    },

    //

    get residueType () {
        return this.residueMap.get( this.residueStore.residueTypeId[ this.residueIndex ] );
    },
    get atomType () {
        return  this.atomMap.get( this.atomStore.atomTypeId[ this.index ] );
    },
    get residueAtomOffset () {
        return this.residueStore.atomOffset[ this.residueIndex ];
    },

    //

    get resname () {
        return this.residueType.resname;
    },
    get hetero () {
        return this.residueType.hetero;
    },

    //

    get atomname () {
        return this.atomType.atomname;
    },
    get element () {
        return this.atomType.element;
    },
    get vdw () {
        return this.atomType.vdw;
    },
    get covalent () {
        return this.atomType.covalent;
    },

    //

    get x () {
        return this.atomStore.x[ this.index ];
    },
    set x ( value ) {
        this.atomStore.x[ this.index ] = value;
    },

    get y () {
        return this.atomStore.y[ this.index ];
    },
    set y ( value ) {
        this.atomStore.y[ this.index ] = value;
    },

    get z () {
        return this.atomStore.z[ this.index ];
    },
    set z ( value ) {
        this.atomStore.z[ this.index ] = value;
    },

    get serial () {
        return this.atomStore.serial[ this.index ];
    },
    set serial ( value ) {
        this.atomStore.serial[ this.index ] = value;
    },

    get bfactor () {
        return this.atomStore.bfactor[ this.index ];
    },
    set bfactor ( value ) {
        this.atomStore.bfactor[ this.index ] = value;
    },

    get occupancy () {
        return this.atomStore.occupancy[ this.index ];
    },
    set occupancy ( value ) {
        this.atomStore.occupancy[ this.index ] = value;
    },

    get altloc () {
        return this.atomStore.getAltloc( this.index );
    },
    set altloc ( value ) {
        this.atomStore.setAltloc( this.index, value );
    },

    //

    eachBond: function( callback ){

        var bp = this.structure._bp;
        var idx = this.index;
        var bondHash = this.bondHash;
        var indexArray = bondHash.indexArray;
        var n = bondHash.countArray[ idx ];
        var offset = bondHash.offsetArray[ idx ];

        for( var i = 0; i < n; ++i ){
            bp.index = indexArray[ offset + i ];
            callback( bp );
        }

    },

    eachBondedAtom: function( callback ){

        var ap = this.structure._ap;
        var idx = this.index;

        this.eachBond( function( bp ){
            if( idx !== bp.atomIndex1 ){
                ap.index = bp.atomIndex1;
            }else{
                ap.index = bp.atomIndex2;
            }
            callback( ap );
        } );

    },

    //

    isBackbone: function(){
        var backboneIndexList = this.residueType.backboneIndexList;
        // console.log(backboneIndexList)
        if( backboneIndexList.length > 0 ){
            var atomOffset = this.residueStore.atomOffset[ this.residueIndex ];
            return backboneIndexList.indexOf( this.index - atomOffset ) !== -1;
        }else{
            return false;
        }
    },

    isPolymer: function(){
        var moleculeType = this.residueType.moleculeType;
        return (
            moleculeType === ProteinType ||
            moleculeType === RnaType ||
            moleculeType === DnaType
        );
    },

    isSidechain: function(){
        return this.isPolymer() && !this.isBackbone();
    },

    isCg: function(){
        var backboneType = this.residueType.backboneType;
        return (
            backboneType === CgProteinBackboneType ||
            backboneType === CgRnaBackboneType ||
            backboneType === CgDnaBackboneType
        );
    },

    isHetero: function(){
        return this.residueType.hetero === 1;
    },

    isProtein: function(){
        return this.residueType.moleculeType === ProteinType;
    },

    isNucleic: function(){
        var moleculeType = this.residueType.moleculeType;
        return (
            moleculeType === RnaType ||
            moleculeType === DnaType
        );
    },

    isRna: function(){
        return this.residueType.moleculeType === RnaType;
    },

    isDna: function(){
        return this.residueType.moleculeType === DnaType;
    },

    isWater: function(){
        return this.residueType.moleculeType === WaterType;
    },

    isIon: function(){
        return this.residueType.moleculeType === IonType;
    },

    isSaccharide: function(){
        return this.residueType.moleculeType === SaccharideType;
    },

    isRing: function(){
        var ringFlags = this.residueType.getRings().flags;
        return ringFlags[ this.index - this.residueAtomOffset ] === 1;
    },

    distanceTo: function( atom ){
        var taa = this.atomStore;
        var aaa = atom.atomStore;
        var ti = this.index;
        var ai = atom.index;
        var x = taa.x[ ti ] - aaa.x[ ai ];
        var y = taa.y[ ti ] - aaa.y[ ai ];
        var z = taa.z[ ti ] - aaa.z[ ai ];
        var distSquared = x * x + y * y + z * z;
        return Math.sqrt( distSquared );
    },

    connectedTo: function( atom ){

        var taa = this.atomStore;
        var aaa = atom.atomStore;
        var ti = this.index;
        var ai = atom.index;

        if( taa.altloc && aaa.altloc ){
            var ta = taa.altloc[ ti ];  // use Uint8 value to compare
            var aa = aaa.altloc[ ai ];  // no need to convert to char
            // 0 is the Null character, 32 is the space character
            if( !( ta === 0 || aa === 0 || ta === 32 || aa === 32 || ( ta === aa ) ) ) return false;
        }

        var x = taa.x[ ti ] - aaa.x[ ai ];
        var y = taa.y[ ti ] - aaa.y[ ai ];
        var z = taa.z[ ti ] - aaa.z[ ai ];

        var distSquared = x * x + y * y + z * z;

        // if( this.residue.isCg() ) console.log( this.qualifiedName(), Math.sqrt( distSquared ), distSquared )
        if( distSquared < 64.0 && this.isCg() ) return true;

        if( isNaN( distSquared ) ) return false;

        var d = this.covalent + atom.covalent;
        var d1 = d + 0.3;
        var d2 = d - 0.5;

        return distSquared < ( d1 * d1 ) && distSquared > ( d2 * d2 );

    },

    positionFromArray: function( array, offset ){

        if( offset === undefined ) offset = 0;

        this.x = array[ offset + 0 ];
        this.y = array[ offset + 1 ];
        this.z = array[ offset + 2 ];

        return this;

    },

    positionToArray: function( array, offset ){

        if( array === undefined ) array = [];
        if( offset === undefined ) offset = 0;

        var index = this.index;
        var atomStore = this.atomStore;

        array[ offset + 0 ] = atomStore.x[ index ];
        array[ offset + 1 ] = atomStore.y[ index ];
        array[ offset + 2 ] = atomStore.z[ index ];

        return array;

    },

    positionToVector3: function( v ){

        if( v === undefined ) v = new Vector3();

        v.x = this.x;
        v.y = this.y;
        v.z = this.z;

        return v;

    },

    positionFromVector3: function( v ){

        this.x = v.x;
        this.y = v.y;
        this.z = v.z;

        return this;

    },

    /**
     * Get intra group/residue bonds
     * @param  {Boolean} firstOnly - immediately return the first connected atomIndex
     * @return {Integer[]|Integer|undefined} connected atomIndices
     */
    getResidueBonds: function( firstOnly ){

        var residueAtomOffset = this.residueAtomOffset;
        var relativeIndex = this.index - this.residueAtomOffset;
        var bonds = this.residueType.getBonds();
        var atomIndices1 = bonds.atomIndices1;
        var atomIndices2 = bonds.atomIndices2;
        var idx1, idx2, connectedAtomIndex, connectedAtomIndices;

        if( !firstOnly ) connectedAtomIndices = [];

        idx1 = atomIndices1.indexOf( relativeIndex );
        while( idx1 !== -1 ){
            connectedAtomIndex = atomIndices2[ idx1 ] + residueAtomOffset;
            if( firstOnly ) return connectedAtomIndex;
            connectedAtomIndices.push( connectedAtomIndex );
            idx1 = atomIndices1.indexOf( relativeIndex, idx1 + 1 );
        }

        idx2 = atomIndices2.indexOf( relativeIndex );
        while( idx2 !== -1 ){
            connectedAtomIndex = atomIndices1[ idx2 ] + residueAtomOffset;
            if( firstOnly ) return connectedAtomIndex;
            connectedAtomIndices.push( connectedAtomIndex );
            idx2 = atomIndices2.indexOf( relativeIndex, idx2 + 1 );
        }

        return connectedAtomIndices;

    },

    //

    qualifiedName: function( noResname ){
        var name = "";
        if( this.resname && !noResname ) name += "[" + this.resname + "]";
        if( this.resno !== undefined ) name += this.resno;
        if( this.inscode ) name += "^" + this.inscode;
        if( this.chainname ) name += ":" + this.chainname;
        if( this.atomname ) name += "." + this.atomname;
        if( this.altloc ) name += "%" + this.altloc;
        if( this.structure.modelStore.count > 1 ) name += "/" + this.modelIndex;
        return name;
    },

    clone: function(){

        return new this.constructor( this.structure, this.index );

    },

    toObject: function(){

        return {
            index: this.index,
            residueIndex: this.residueIndex,

            atomno: this.atomno,
            resname: this.resname,
            x: this.x,
            y: this.y,
            z: this.z,
            element: this.element,
            chainname: this.chainname,
            resno: this.resno,
            serial: this.serial,
            vdw: this.vdw,
            covalent: this.covalent,
            hetero: this.hetero,
            bfactor: this.bfactor,
            altloc: this.altloc,
            atomname: this.atomname,
            modelindex: this.modelindex
        };

    }

};

export default AtomProxy;
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name ng.directive:ngInit
 * @restrict AC
 *
 * @description
 * The `ngInit` directive allows you to evaluate an expression in the
 * current scope.
 *
 *

 * The only appropriate use of `ngInit` is for aliasing special properties of
 * {@link api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
 * should use {@link guide/controller controllers} rather than `ngInit`
 * to initialize values on a scope.
 *

 *
 * @priority 450
 *
 * @element ANY
 * @param {expression} ngInit {@link guide/expression Expression} to eval.
 *
 * @example
   <doc:example>
     <doc:source>

          list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};

     </doc:source>
     <doc:scenario>
       it('should alias index positions', function() {
         expect(element('.example-init').text())
           .toBe('list[ 0 ][ 0 ] = a;' +
                 'list[ 0 ][ 1 ] = b;' +
                 'list[ 1 ][ 0 ] = c;' +
                 'list[ 1 ][ 1 ] = d;');
       });
     </doc:scenario>
   </doc:example>
 */
var ngInitDirective = ngDirective({
  priority: 450,
  compile: function() {
    return {
      pre: function(scope, element, attrs) {
        scope.$eval(attrs.ngInit);
      }
    };
  }
});
<commit_msg>docs(ngInit): note precedence when used with filters<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name ng.directive:ngInit
 * @restrict AC
 *
 * @description
 * The `ngInit` directive allows you to evaluate an expression in the
 * current scope.
 *
 *

 * The only appropriate use of `ngInit` is for aliasing special properties of
 * {@link api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
 * should use {@link guide/controller controllers} rather than `ngInit`
 * to initialize values on a scope.
 *

 *

 * **Note**: If you have assignment in `ngInit` along with {@link api/ng.$filter `$filter`}, make
 * sure you have parenthesis for correct precedence:
 *

 *   <ng-init="test1 = (data | orderBy:'name')">
 *

 *

 *
 * @priority 450
 *
 * @element ANY
 * @param {expression} ngInit {@link guide/expression Expression} to eval.
 *
 * @example
   <doc:example>
     <doc:source>

          list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};

     </doc:source>
     <doc:scenario>
       it('should alias index positions', function() {
         expect(element('.example-init').text())
           .toBe('list[ 0 ][ 0 ] = a;' +
                 'list[ 0 ][ 1 ] = b;' +
                 'list[ 1 ][ 0 ] = c;' +
                 'list[ 1 ][ 1 ] = d;');
       });
     </doc:scenario>
   </doc:example>
 */
var ngInitDirective = ngDirective({
  priority: 450,
  compile: function() {
    return {
      pre: function(scope, element, attrs) {
        scope.$eval(attrs.ngInit);
      }
    };
  }
});
<|endoftext|>